summaryrefslogtreecommitdiff
path: root/test/udevd-test/udevd-test.pl
blob: 060c3ec5f2cbdd5075898932ed3131de30f290d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
#!/usr/bin/perl -w
#
# udevd-test
#
# Copyright (C) Intel Corp, 2004
#
# Author: Yin Hu <hu.yin@intel.com> 
#	  Kay Sievers <kay.sievers@vrfy.org>
#
# Provides automated testing of the udevd binary.This test script is self-contained.
# Before you run this script please modify $sysfs to locate your sysfs filesystem, 
# modify $udevd_bin to locate your udevsend binary,
# modify $udev_bin to locate dummy udev script,
# modify $udev_bin2 to locate another dummy udev script ( amplify the execution time for test),
# modify $log_file to locate where udev script have placed the log file,
# modify $time_out to decide the time out for events,
# modify $udev_exe_time to decide the execution time for dummy udev script.
#
# Detail information of each test case please refer to the header of corresponding
# test case function.
#
#
#	This program is free software; you can redistribute it and/or modify it
#	under the terms of the GNU General Public License as published by the
#	Free Software Foundation version 2 of the License.
#
#	This program is distributed in the hope that it will be useful, but
#	WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#	General Public License for more details.
#
#	You should have received a copy of the GNU General Public License along
#	with this program; if not, write to the Free Software Foundation, Inc.,
#	675 Mass Ave, Cambridge, MA 02139, USA.
#

use warnings;
use strict;

# modifiable settings
my $sysfs     = "../sys";
my $udevd_bin = "../../udevsend";
my $udev_bin  = "$ENV{PWD}/udev-log-script.pl";
my $udev_bin2 = "$ENV{PWD}/udev-log-amplify.pl";
my $log_file  = "/tmp/udev_log.txt";
my $time_out  = 10;
my $udev_exe_time = 5;

# global variables
my $test_case = 0;

# common functions

sub kill_daemon {
	system("killall udevd");
	system("rm -f $log_file");
	sleep 1;
}

sub udevsend {
	# This function prepares corresponding environment variables
	# and then call $udevd_bin to send event.

	my ($seqnum, $devpath, $action, $subsystem, $udev_bin_tmp) = @_;

	$ENV{DEVPATH} = $devpath;
	$ENV{ACTION} = $action;
	$udev_bin_tmp = $udev_bin if ( not $udev_bin_tmp );
	$ENV{UDEV_BIN} = $udev_bin_tmp;
	if ( $seqnum != -1) {
		$ENV{SEQNUM} = $seqnum;
	} else {
		delete $ENV{SEQNUM};
	}

	return system("$udevd_bin $subsystem");
}

sub getDate {
	# Get current date function
	# If we want GTM time, simply pass GMT as first argument to this function.

	my $format = @_;
	my $date;

	if( $format =~ /GMT/i ) {
		$date = gmtime() . " GMT";
	} else {
		$date = localtime();
	}

	return $date;
}

sub cmpDate {
	# This function should return a difference betweent date1 and date2

	my ($date1, $date2) = @_;
	my @monList = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
			  "Aug", "Sep", "Oct", "Nov", "Dec" );
	my ( $m1, $m2, $tmp );

	$date1 =~ s/([\D]*)$//g;
	$date2 =~ s/([\D]*)$//g;

	return if( (not $date1) or (not $date2) );

	my $mon = 0;

	my ( @T1 ) =
	 ( $date1 =~ /([\d]+)[\s]+([\d]+):([\d]+):([\d]+)[\s]+([\d]+)/g );
	my ( @T2 ) = 
	 ( $date2 =~ /([\d]+)[\s]+([\d]+):([\d]+):([\d]+)[\s]+([\d]+)/g );

	foreach $tmp (@monList) {
		$m1 = sprintf("%2.2d",$mon) if( $date1 =~ /$tmp/i );
		$m2 = sprintf("%2.2d",$mon) if( $date2 =~ /$tmp/i );
		$mon++;
	}

	my $dt1 = sprintf("%4.4d%s%2.2d%2.2d%2.2d%2.2d", $T1[4], $m1, $T1[0],
					$T1[1], $T1[2], $T1[3]);
	my $dt2 = sprintf("%4.4d%s%2.2d%2.2d%2.2d%2.2d", $T2[4], $m2, $T2[0],
					$T2[1], $T2[2], $T2[3]);

	my $ret = $dt1 - $dt2;

	if ( $ret > 40 ) {
		$ret = abs($ret-40);
	}

	return $ret;
}

sub check_count_and_time { 
	my $event_recv_time;
	my $udev_fork_time;
	my $log_ln_count = 0;
	my $line;
	my @content;
	my @line_items;
	my $diff;

	($event_recv_time) = @_;

	print "   event receiving time:  $event_recv_time\n\n";

	open(LOGF, $log_file) || die "Opening file $log_file: $!";
	@content = <LOGF>;
	foreach $line ( @content ) {
		@line_items = split(/,/,$line);
		print "   device: $line_items[0], action: $line_items[1] \n";
		print "   forking udev time:     $line_items[-1]";
		$diff = cmpDate($line_items[-1], $event_recv_time);
		print "   the delay time is:     $diff s \n\n";
		if ( $diff > $time_out ) {
			print "   the delay time is: $diff \n";
			print "   udevd doesn't act properly. \n";
			exit 1;
		}
		$log_ln_count++;
	}
	close(LOGF);

	return $log_ln_count;
}

sub check_sysfs_device_exist {
	# check if the designated devices exist
	my @dev_list = @_;
	my $dev;

	foreach $dev (@dev_list) {
		if (! -e $dev) {
			print "the designated device $dev doesn't exist. please change a device!\n";
			exit 1;
		}
	}
}

sub show_result {
	my $event_recv_time;
	my $udev_fork_time;
	my $line;
	my @content;
	my @line_items;
	my $diff;

	($event_recv_time) = @_;

	print "   event receiving time:  $event_recv_time\n\n";

	open(LOGF, $log_file) || die "Opening file $log_file: $!";
	@content = <LOGF>;
	foreach $line ( @content ) {
		@line_items = split(/,/,$line);
		print "   device: $line_items[0], action: $line_items[1] \n";
		print "   forking udev time:     $line_items[-1]";
		$diff = cmpDate($line_items[-1], $event_recv_time);
		print "   the delay time is:     $diff s \n\n";
	}
	close(LOGF);
}

sub show_result_tm_out {
	my $event_recv_time;
	my $udev_fork_time;
	my $line;
	my @content;
	my @line_items;
	my $diff;

	($event_recv_time) = @_;

	print "   event receiving time:  $event_recv_time\n\n";

	open(LOGF, $log_file) || die "Opening file $log_file: $!";
	@content = <LOGF>;
	foreach $line ( @content ) {
		@line_items = split(/,/,$line);
		print "   device: $line_items[0], action: $line_items[1] \n";
		print "   forking udev time:     $line_items[-1]";
		$diff = cmpDate($line_items[-1], $event_recv_time);
		print "   the delay time is:     $diff s \n\n";
		if ( $diff < $time_out ) {
			print "   the delay time is:     $diff \n";
			print "   udevd doesn't act properly. \n";
			exit 1;
		}
	}
	close(LOGF);
}

sub show_result_immediate {
	my $event_recv_time;
	my $udev_fork_time;
	my $line;
	my @content;
	my @line_items;
	my $diff;

	($event_recv_time) = @_;

	print "   event receiving time:  $event_recv_time\n\n";

	open(LOGF, $log_file) || die "Opening file $log_file: $!";
	@content = <LOGF>;
	foreach $line ( @content ) {
		@line_items = split(/,/,$line);
		print "   device: $line_items[0], action: $line_items[1] \n";
		print "   forking udev time:     $line_items[-1]";
		$diff = cmpDate($line_items[-1], $event_recv_time);
		print "   the delay time is:     $diff s \n\n";
		if ( $diff > $time_out ) {
			print "   the delay time is:     $diff \n";
			print "   udevd doesn't act properly. \n";
			exit 1;
		}
	}
	close(LOGF);
}

sub check_exe_time {
	my @exe_time;
	my $i = 0;
	my $line;
	my @content;
	my @line_items;
	my $diff;

	open(LOGF, $log_file) || die "Opening file $log_file: $!";
	@content = <LOGF>;
	close(LOGF);
	foreach $line ( @content ) {
		@line_items = split(/,/,$line);
		$exe_time[$i] = $line_items[-1];
		$i++;
	}
	$diff = cmpDate($exe_time[1], $exe_time[0]);
	if ( $diff < $udev_exe_time ) {
		print "   there are more than one udev instance for a single device at the same time. \n";
		exit 1;
	} else {
		print "   there is just one udev instance for a single device at the same time. \n";
	}
}

# test case functions
sub run_no_seq_test {
	print "Test case name:     no sequence number test\n";
	print "Test case purpose:  check whether udevd forks udev immediately when environment variable SEQNUM is null.\n";
	print "Test expected visible results: \n";
	print "   the delay time between event receiving and forking udev for udevd should be negligible, \n";
	print "   that is, udev should be forked at once. please notice the following time...\n\n";

	# local variables
	my $time;

	#
	# add devices event test
	#
	kill_daemon();

	# check if devices /block/sda exist
	check_sysfs_device_exist("$sysfs/block/sda");

	# log current system date/time
	$time = getDate();

	# fork udevd
	udevsend(-1, "/block/sda", "add", "block");

	# check if execution is successful in time
	sleep 1;
	show_result_immediate($time);
	print "   fork udev (add device) at once successfully.\n\n";

	#
	# remove devices event test
	#
	system("rm -f $log_file");

	# log current system date/time
	$time = getDate();

	# fork udevd
	udevsend(-1, "/block/sda", "remove", "block");

	# check if execution is successful in time
	sleep 1;
	show_result_immediate($time);
	print "   fork udev (remove device) at once successfully.\n\n";
	print "this case is ok\n\n";
}

sub run_normal_seq_test {
	print "Test case name:    normal sequence number stress test\n";
	print "Test case purpose: check whether udevd can fork massive udev instances for \n";
	print "                   massive sequential events successfully. \n";
	print "Test expected visible results: \n";
	print "   Populate all the devices in directory $sysfs/class/tty, fork udved to send add/remove \n";
	print "   event to udev for each device. \n";
	print "   We can see the delay time for each device should be negligible. \n\n";

	# local variables
	my @file_list;
	my $file;
	my $seq = 0;
	my $time;
	my $ret_seq;

	# prepare
	kill_daemon();
	@file_list = glob "$sysfs/class/tty/*";

	# log current system date/time for device add events
	$time = getDate();

	#
	# add devices event test
	#
	print "add device events test: \n";
	foreach $file (@file_list) {
		udevsend($seq, substr($file, length($sysfs), length($file)-length($sysfs)), "add", "tty");
		# check if execution is successful
		if ($? == 0) {
			$seq++;
		} else {
			print "add event: error\n\n";
			exit 1;
		}
	}

	# we'd better wait the udev to create all the device for a few seconds
	print "   wait for udevd processing about $time_out s... \n\n";
	sleep $time_out;

	$ret_seq = check_count_and_time($time);
	if ( $ret_seq != $seq ) {
		print "   add event: failed. some device-adding events fail to execute.\n\n";
		exit 1;
	} else {
		print "   $seq pieces of device-adding events have executed successfully.\n\n";
	}

	# log current system date/time for device remove events
	$time = getDate();

	#
	# remove devices event test
	#
	print "remove device events test: \n";
	kill_daemon();
	@file_list = glob "$sysfs/class/tty/*"; 
	$seq = 0;
	foreach $file (@file_list) {
		udevsend($seq, substr($file, length($sysfs), length($file)-length($sysfs)), "remove", "tty");
		# check if execution is successful
		if ($? == 0) {
			$seq++;
		} else {
			print "remove event: error\n\n";
			exit 1;
		}
	}

	# we'd better wait the udev to create all the device for a few seconds
	print "   waiting for udev removing devices (about $time_out s)...\n";
	sleep $time_out;

	# show results
	$ret_seq = check_count_and_time($time);
	if ( $ret_seq != $seq ) {
		print "   remove event: failed. some device-removing events fail to execute.\n\n";
		exit 1;
	} else {
		print "   $seq pieces of device-removing events have executed successfully.\n\n";
		print "this case is ok.\n\n";
	}
}

sub run_random_seq_test {
	print "Test case name:    random sequence number test case,\n";
	print "Test case purpose: check whether udevd can order the events with random sequence number \n";
	print "                   and fork udev correctly. \n";
	print "Test expected visible results: \n";
	print "   We have disordered the events sent to udevd, if udevd can order them correctly, the devices' \n";
	print "   add/remove sequence should be tty0, tty1, tty2. \n\n";

	# local variables
	my $time;

	# check if devices /class/tty/tty0, tty1, tty2 exist
	check_sysfs_device_exist("$sysfs/class/tty/tty0", "$sysfs/class/tty/tty1", "$sysfs/class/tty/tty2");

	#
	# add device events test
	#
	print "add device events test: \n";
	kill_daemon();

	# log current system date/time for device remove events
	$time = getDate();

	# parameters: 1 sequence number, 2 device, 3 action, 4 subsystem
	udevsend(3, "/class/tty/tty2", "add", "tty");
	udevsend(1, "/class/tty/tty0", "add", "tty");
	udevsend(2, "/class/tty/tty1", "add", "tty");
	print "   wait for udevd processing about $time_out s... \n\n";
	sleep $time_out+1;
	show_result_tm_out($time);

	#
	# remove device events test
	#
	print "\nremove device events test: \n";
	kill_daemon();

	# log current system date/time for device remove events
	$time = getDate();

	# fork udevd
	udevsend(3, "/class/tty/tty2", "remove", "tty");
	udevsend(2, "/class/tty/tty1", "remove", "tty");
	udevsend(1, "/class/tty/tty0", "remove", "tty");

	# show results
	print "   wait for udevd processing about $time_out s... \n\n";
	sleep $time_out+1;
	show_result_tm_out($time);
	print "this case is ok.\n\n";
}

sub run_expected_seq_test { 
	print "Test case name:    expected sequence number test \n";
	print "Test case purpose: check whether udevd fork udev immediately when the incoming event\n";
	print "                   is exactly the expected event sequence number.\n";
	print "Test expected visible results:\n";
	print "   first, udevd disposes disorder events(sequence number is 3,1,2,5,4,6),\n";
	print "   thus after disposed the expected event number for udevd is 7, when incoming event is 7, udevd\n";
	print "   should fork udev immediately, the delay time should be negligible. \n";
	print "   where: event 7 is (add device /class/tty/tty2) \n\n";

	# local variables
	my $time;

	# check if devices /class/tty0, tty1, tty2 exist
	check_sysfs_device_exist("$sysfs/class/tty/tty0", "$sysfs/class/tty/tty1", "$sysfs/class/tty/tty2");

	# prepare
	kill_daemon();

	# parameters: 1 sequence number, 2 device, 3 action, 4 subsystem
	udevsend(3, "/class/tty/tty2", "add", "tty");
	udevsend(1, "/class/tty/tty0", "add", "tty");
	udevsend(2, "/class/tty/tty1", "add", "tty");
	udevsend(5, "/class/tty/tty1", "remove", "tty");
	udevsend(4, "/class/tty/tty0", "remove", "tty");
	udevsend(6, "/class/tty/tty2", "remove", "tty");

	print "   wait for udevd timing out for disorder events (about $time_out s) \n\n";
	sleep $time_out+1;
	system("rm -f $log_file");

	# log current system date/time for device remove events
	$time = getDate();

	# show results
	udevsend(7, "/class/tty/tty2", "add", "tty");
	sleep 1;
	print "   event sequence number: 7 \n";
	show_result_immediate($time);

	print "this case is ok.\n\n";
}

sub run_single_instance_test { 
	print "Test case name:    single instance running for a single device test \n";
	print "Test case purpose: check whether udevd only fork one udev instance for a single\n";
	print "                   device at the same time. For each event a udev instance is \n";
	print "                   executed in the background. All further events for the same \n";
	print "                   device are delayed until the execution is finished. This way \n";
	print "                   there will never be more than one instance running for a single \n";
	print "                   device at the same time.\n";
	print "Test expected visible results:\n";
	print "   In this test we amplify the execution time of udev (about 5 seconds), first, \n";
	print "   we send a add event for device /block/sda, and then we send a remove event, so the \n";
	print "   execution of remove event should be delayed until add is finished. \n\n";

	# local variables
	my $time;

	# prepare
	kill_daemon();

	# check if device exists
	check_sysfs_device_exist("$sysfs/block/sda");

	# log current system date/time
	$time = getDate();

	# fork udved
	udevsend(-1, "/block/sda", "add", "block", $udev_bin2);
	udevsend(-1, "/block/sda", "remove", "block", $udev_bin2);

	# show results
	print "   wait for udevd processing about $udev_exe_time s... \n\n";
	sleep $udev_exe_time+1;
	show_result_immediate($time);
	check_exe_time();
	print "this case is ok\n\n";
}

sub run_same_events_test { 
	print "Test case name:    event sequence number overlap test \n";
	print "Test case purpose: check whether udevd doesn't fork udev untill time out\n";
	print "                   when encountering a event with sequence number same as the pevious event. \n";
	print "Test expected visible results:\n";
	print "   event ( remove device /block/sda ) should be no delay, \n";
	print "   event ( add device /class/tty/tty1 ) should be delayed for $time_out s than its previous \n";
	print "   event ( remove device /block/sda ) \n\n";

	# local variables
	my $time;

	# prepare
	kill_daemon();

	# check if device exist
	check_sysfs_device_exist("$sysfs/block/sda", "$sysfs/class/tty/tty1");

	# fork udevd
	udevsend(0, "/block/sda", "add", "block");

	# log current system date/time
	sleep 1;
	$time = getDate();
	system("rm -f $log_file");

	# fork udevd
	udevsend(1, "/block/sda", "remove", "block");
	udevsend(1, "/class/tty/tty1", "add", "tty");

	# show results
	print "   wait for udevd processing about $time_out s... \n\n";
	sleep $time_out+1;
	show_result($time);
	print "this case is ok\n\n";
}

sub run_missing_seq_test {
	print "Test case name:    missing sequence number test \n";
	print "Test case purpose: check whether udevd doesn't fork udev untill time out\n";
	print "                   when certain event sequence number is missing.\n";
	print "Test expected visible results:\n";
	print "   the delay time for event(add device /block/sda) should be about $time_out s.\n\n";

	# local variables
	my $time;

	# prepare
	kill_daemon();

	# check if device exist
	check_sysfs_device_exist("$sysfs/block/sda", "$sysfs/class/tty/tty1");

	# fork udevd
	udevsend(0, "/class/tty/tty1", "add", "tty");
	udevsend(1, "/class/tty/tty1", "remove", "tty");
	sleep 1;

	# log current system date/time
	$time = getDate();
	system("rm -f $log_file");

	# fork udevd
	udevsend(3, "/block/sda", "add", "block");

	# show results
	print "   wait for udevd processing about $time_out s... \n\n";
	sleep $time_out+1;
	show_result($time);
	print "this case is ok\n\n";
}

sub run_all_cases_test { 
	run_no_seq_test();
	run_normal_seq_test();
	run_random_seq_test();
	run_missing_seq_test();
	run_expected_seq_test();
	run_same_events_test();
	run_single_instance_test();
}

# main program
if ($ARGV[0]) {
	$test_case = $ARGV[0];

	if ($test_case == 1) {
		run_no_seq_test();
	} elsif ($test_case == 2) {
		run_normal_seq_test();
	} elsif ($test_case == 3) {
		run_random_seq_test();
	} elsif ($test_case == 4) {
		run_missing_seq_test();
	} elsif ($test_case == 5) {
		run_expected_seq_test();
	} elsif ($test_case == 6) {
		run_single_instance_test();
	} elsif ($test_case == 7) {
		run_same_events_test();
	} else {
		run_all_cases_test();
	}
} else {
	# echo usage
	print "command format: perl udevd-test.pl <case number>\n";
	print "   test case:\n";
	print "           1: no event sequence number\n";
	print "           2: sequential event sequence number\n";
	print "           3: random event sequence number\n";
	print "           4: missing event sequence number\n";
	print "           5: the incoming event sequence number is right the expected sequence number\n";
	print "           6: single udev instance on a single device at the same time\n";
	print "           7: test event sequence number overlap\n";
	print "           9: all the cases\n\n";
}