9 local $SIG{__WARN__
} = \
&Carp
::cluck
;
12 use List
::MoreUtils qw
/any uniq/;
14 my $runtest_dir="/usr/lib/ltp/runtest";
15 #my $runtest_dir="runtest";
17 LTPRESULTS
=> "/var/cache/ltp/results",
18 LTPROOT
=> "/usr/lib/ltp",
19 TOOLSDIR
=> "/usr/lib/ltp/tools",
20 LTPTOOLS
=> "/usr/lib/ltp/tools",
21 PATH
=> "$ENV{PATH}:/usr/lib/ltp/testcases/bin",
22 TMP
=> $ENV{TMP
} // $ENV{TMPDIR
} // "/tmp",
23 TMPDIR
=> $ENV{TMP
} // $ENV{TMPDIR
} // "/tmp",
38 return $arg if $arg =~ /^\d+$/;
44 my ($used_tests, $environment, $setup, $teardown) = @_;
47 for(keys %$environment) {
49 my $value=$environment->{$_};
54 for(keys %static_env) {
55 my $value=$static_env{$_};
58 if(($environment->{output
} // "") ne "") {
59 $tdir=File
::Temp
::tempdir
(CLEANUP
=> 1);
60 $panopts.=" -o ".shell_quote
($environment->{output
})." -O ".shell_quote
($tdir);
63 if(!$environment->{fulloutput
}) {
67 if(($environment->{failfile
} // "") ne "") {
68 my $ff=$environment->{failfile
};
69 my $out=$environment->{output
}//"";
74 $panopts.=" -C ".shell_quote
($ff);
77 if(($environment->{logfile
} // "") ne "") {
78 my $ff=$environment->{logfile
};
79 my $out=$environment->{output
}//"";
84 $panopts.=" -l ".shell_quote
($ff);
88 if(($environment->{count
} // "") ne "") {
89 $count=$environment->{count
};
90 if($count =~ /^(\d)l$/) {
91 $count=$1*@
$used_tests;
93 $count=to_number
($count);
95 if($environment->{random
}) {
96 $count = $count // scalar @
$used_tests;
99 if(!($environment->{random
})) {
103 if(defined($count)) {
104 $panopts.=" -s ".$count;
107 if(($environment->{time} // "") ne "") {
108 return if !$environment->{time} =~ /[0-9]+[hmsd]?/;
109 $panopts.=" -t ".$environment->{time};
112 if(($environment->{parallel
} // "") ne "") {
113 $panopts.=" -x ".to_number
($environment->{parallel
} // 0);
116 my $ft=new File
::Temp
(TEMPLATE
=> "/tmp/runtestXXXXXXXXX");
117 my $zoo=new File
::Temp
(TEMPLATE
=> "/tmp/zoo$$.XXXXXXXXX");
119 print $ft map { "$_->{id} $_->{command}" } @
$used_tests;
122 system '${LTPTOOLS}/ltp-pan -p -n $$ -a '.$zoo->filename.' -f '.$ft->filename.$panopts;
124 File
::Path
::rmtree
$tdir if defined($tdir);
133 open(FILE
, "<$file");
135 my $ts_desc={ selected
=> 0, tests
=> [] };
141 if($1 =~ /DESCRIPTION:(.*)/) {
142 $ts_desc->{description
}=$1;
143 $last=\
$ts_desc->{description
};
144 } elsif ($1 =~ /ALL:(.*)/) {
145 push @
$allvars, grep m/./, (split qr/ +/, $1);
146 } elsif ($1 =~ /SETUP:(.*)/) {
147 push @
$allsetup, grep m/./, (split qr/ +/, $1);
150 } elsif(defined $last) {
160 next unless /^([^[:space:]]+)[[:space:]]+(.*)/;
162 if(exists($test_hash{"$1 $2"})) {
163 next if exists $test_hash{"$1 $2"}->{files
}->{$name};
164 push @
{$ts_desc->{tests
}}, $test_hash{"$1 $2"};
165 $test_hash{"$1 $2"}->{files
}->{$name}=undef;
166 $ts_desc->{selected
}++ if($test_hash{"$1 $2"}->{selected
});
168 push(@test_data, { id
=> $1, command
=> $2,
169 files
=> {$name => undef}});
171 $test_data[$#test_data]->{vars
}=[(grep { /^\$(.*)/; !exists($static_env{$1}) } ( @
$allvars, $2 =~ /\$[[:alpha:]][[:alnum:]_]*/g ))];
172 $test_data[$#test_data]->{setup
}=$allsetup;
173 push @
{$ts_desc->{tests
}}, \
%{$test_data[$#test_data]};
174 $test_hash{$id}=\
%{$test_data[$#test_data]};
175 $testsuites{"<ALL>"}->{count
}++;
180 $ts_desc->{count
}=$num;
181 $testsuites{$name}=$ts_desc;
186 $testsuites{"<ALL>"}={selected
=> 0, count
=> 0, tests
=> \
@test_data};
189 opendir(DIR
, $runtest_dir) || die("Couldn't open directory $runtest_dir");
195 load_file
("$runtest_dir/$_");
202 return map { (ref($_) eq "ARRAY") ? flatten
(@
$_) : $_ } @_;
208 return uniq
(flatten
(map $_->{vars
}, @
$ts_list));
214 return uniq
(flatten
(map $_->{setup
}, @
$ts_list));
217 sub tests_needing_vars
219 my ($tests, @vars)=@_;
220 my %vars_hash=(map { ($_ => undef) } @vars);
221 return grep { any
{ exists($vars_hash{$_}) } @
{$_->{vars
}} } @
$tests;
226 package Layout
::Proportional
;
228 use Curses
::UI
::Container
;
230 our @ISA=qw
/Curses::UI::Container/;
234 my $self = $class->SUPER::new
( @_, -layoutorder
=> [] );
240 my $ret=$self->SUPER::add
(@_);
241 push @
{$self->{-layoutorder
}}, $self->{-object2id
}->{$ret};
247 $self->Curses::UI
::Widget
::layout
();
249 my $size=($self->{-vertical
} ?
"height" : "width");
250 my $coord=($self->{-vertical
} ?
"y" : "x");
251 my $ot_size=($self->{-vertical
} ?
"width" : "height");
252 my $ot_sz=($self->{-vertical
} ?
"w" : "h");
253 my $ot_coord=($self->{-vertical
} ?
"x" : "y");
254 my $padding=($self->{-vertical
} ?
255 sub { my $obj=shift; return ($obj->{-padtop
}//0)+($obj->{-padbottom
}//0); } :
256 sub { my $obj=shift; return ($obj->{-padleft
}//0)+($obj->{-padright
}//0); });
259 my %sizes= ( height
=> $self->canvasheight(), width
=> $self->canvaswidth() );
260 foreach(@
{$self->{-layoutorder
}}) {
261 my $obj=$self->{-id2object
}->{$_};
262 # NOTE: if -min-$size is less than what's needed to accommodate the widget, you'll likely hit screen too small.
263 $total_min += $obj->{"-min-$size"} if $obj->{"-min-$size"};
264 $total_mult += $obj->{"-mult-$size"} if $obj->{"-mult-$size"};
266 my $excess = $sizes{$size}-$total_min;
267 my $ratio=($total_mult==0 ?
0 : $excess/$total_mult);
270 foreach(@
{$self->{-layoutorder
}}) {
271 my $obj=$self->{-id2object
}->{$_};
272 $obj->{-$coord}=$cur;
273 $obj->{-$ot_coord}=0;
274 $obj->{-$size}=int (($obj->{"-min-$size"} // 0) + ($obj->{"-mult-$size"} // 0)*$ratio);
275 $obj->{-$size} = 0 if $obj->{-$size} < 0;
276 $obj->{-$ot_size} = $self->{-$ot_size};
277 $obj->{-parent
}=$self;
279 $cur += $obj->{-$size};
280 $self->{-$ot_sz} = $obj->{-$ot_sz}
281 if ($obj->{-$ot_sz} > $self->{-$ot_sz});
287 $INC{"Layout/Proportional.pm"}="";
289 package ListBox
::MultiColumn
;
291 use Curses
::UI
::Listbox
;
293 our @ISA=qw
/Curses::UI::Listbox/;
297 my $self = $class->SUPER::new
(
299 "-col-designator" => [],
307 my ($self, $col) = @_;
308 return $self->{-colwidth
}->[$col] if $self->{-colwidth
}->[$col];
314 my $val = $self->values()->[$i];
316 if(ref($val) eq "ARRAY") {
317 for my $idx (0..$#$val-1) {
318 my $len=$self->getcolwidth($idx);
319 push (@disp, $$val[$idx]);
321 push (@disp, $$val[$#$val]);
322 } elsif(ref($val) eq "HASH") {
323 my $cd=$self->{"-col-designator"};
324 return "" unless $cd;
325 for my $idx (0..$#$cd-1) {
326 my $len=$self->getcolwidth($idx);
327 push (@disp, $val->{$cd->[$idx]});
329 push (@disp, $val->{$cd->[$#$cd]});
331 @disp=($self->SUPER::getlabel
($i));
333 @disp=($self->{"-col-formatter"} ?
334 $self->{"-col-formatter"}->(@disp) :
337 my $len=$self->getcolwidth($_);
338 $disp[$_] = sprintf("%-${len}.${len}s", $disp[$_]);
340 return join("|", @disp);
343 $INC{"ListBox/MultiColumn.pm"}="";
345 package Container
::Paged
;
347 our @ISA=qw
/Curses::UI::Container/;
351 my $self = $class->SUPER::new
(
359 $self->set_binding(\
&page_up
, Curses
::KEY_PPAGE
);
360 $self->set_binding(\
&page_down
, Curses
::KEY_NPAGE
);
361 $self->{-morelabel
}=$self->SUPER::add
(undef, "Label",
362 -text
=> "-- more (PgDn/PgUp) --"
369 my $ret=$self->SUPER::add
(@_);
370 push @
{$self->{-layoutorder
}}, $ret;
376 return if $self->{-pageno
}<=0;
378 $self->{-canvasscr
}->erase();
380 $self->intellidraw();
385 return if $self->{-pageend
}>$#{$self->{-layoutorder}};
387 $self->{-pagestart
}->[$self->{-pageno
}] = $self->{-pageend
};
388 $self->{-canvasscr
}->erase();
390 $self->intellidraw();
395 $self->Curses::UI
::Widget
::layout
();
398 my $saved_screen_too_small = $Curses::UI
::screen_too_small
;
399 my $last_widget = $#{$self->{-layoutorder}}+1;
400 my $page_start = $self->{-pagestart
}->[$self->{-pageno
}];
401 for my $widget_idx ($page_start..$#{$self->{-layoutorder}}) {
402 my $widget=$self->{-layoutorder
}->[$widget_idx];
407 if($Curses::UI
::screen_too_small
!= $saved_screen_too_small) {
408 $last_widget = $widget_idx;
411 $line += $widget->height();
414 if ($last_widget > $page_start+1 && $last_widget <= $#{$self->{-layoutorder}} && $line>=$self->{-h}) {
418 $self->{-pageend
}=$last_widget;
419 $Curses::UI
::screen_too_small
= $saved_screen_too_small;
422 if ($last_widget <= $#{$self->{-layoutorder}}) {
423 $self->{-morelabel
}->{-x
}=($self->{-w
}-$self->{-morelabel
}->{-w
})/2;
424 $self->{-morelabel
}->{-y
}=$self->{-h
}-1;
425 $self->{-morelabel
}->layout;
426 push @label_arr, $self->{-morelabel
};
429 for (@
{$self->{-layoutorder
}}[0..$page_start-1]) {
433 if ($last_widget >= 0 && $last_widget<=$#{$self->{-layoutorder}}) {
434 for (@
{$self->{-layoutorder
}}[$last_widget..$#{$self->{-layoutorder}}]) {
439 $Curses::UI
::screen_too_small
++
440 if($last_widget > 0 && $page_start==$last_widget);
442 $self->set_draworder(map $self->{-object2id
}->{$_}, @label_arr, @
{$self->{-layoutorder
}}[$page_start..$last_widget-1]);
443 $self->focus($self->{-layoutorder
}->[$page_start])
444 if defined $self->{-layoutorder
}->[$page_start];
450 shift()->{-drawdisabled
}++;
456 $self->{-drawdisabled
}--;
463 return $self->SUPER::draw
(@_)
464 unless $self->{-drawdisabled
};
469 my $page_start=$self->{-pagestart
}->[$self->{-pageno
}];
470 my $last_widget=$self->{-pageend
};
471 my $last_draworder=$self->{-draworder
};
473 return $self->SUPER::focus
(@_) unless $#{$self->{-draworder}}>=0;
476 $self->set_draworder(grep ($self->{-draworder
}->[-1] ne $_, map $self->{-object2id
}->{$_}, @
{$self->{-layoutorder
}}), $self->{-draworder
}->[-1]);
477 $self->SUPER::focus
(@_);
479 my $new_focus=$self->{-object2id
}->{$self->getfocusobj};
481 for my $j (0..$#{$self->{-layoutorder}}) {
483 last if $self->{-object2id
}->{$self->{-layoutorder
}->[$i]} eq $new_focus;
486 if ($i>=$page_start && $i<$last_widget) {
487 $self->set_draworder(grep ($self->{-draworder
}->[-1] ne $_, @
$last_draworder), $self->{-draworder
}->[-1]);
492 if($i>=$last_widget) {
493 while($i>=$self->{-pageend
}) {
497 while($i<$self->{-pagestart
}->[$self->{-pageno
}]) {
506 $INC{"Container/Paged.pm"}="";
509 use List
::MoreUtils qw
/any/;
515 my $cui = new Curses
::UI
(
523 simple_text
(label
=> "Output file", name
=> "output"),
524 simple_text
(label
=> "Log file", name
=> "logfile"),
525 simple_text
(label
=> "Failed command file", name
=> "failfile"),
526 simple_text
(label
=> "Running time", name
=> "time", validator
=> '/^\d*[hdms]?$/'),
527 simple_text
(label
=> "Parallel tests", name
=> "parallel", validator
=> '/^\d*$/'),
528 simple_check
(label
=> "Random tests", name
=> "random"),
529 simple_check
(label
=> "Full Output", name
=> "fulloutput"),
530 simple_text
(label
=> "Test count", name
=> "count", validator
=> '/^\d*l?$/'),
533 my %options =( failfile
=> "/var/tmp/LTP_RUN_ON_%d.failed",
534 logfile
=> "/var/tmp/LTP_RUN_ON_%d.log" );
538 return { label
=> $params{label
}, name
=> $params{name
},
540 my ($parent, $data, @params) = @_;
541 @params=(@params, -regexp
=> $params{validator
}) if exists $params{validator
};
542 my $lab=$parent->add(undef, "TextEntry", @params);
543 $lab->text($data) if defined $data;
546 get
=> sub { return shift()->get(); }
553 return { label
=> $params{label
}, name
=> $params{name
},
555 my ($parent, $data, @params) = @_;
556 my $lab=$parent->add(undef, "Checkbox", @params);
557 $lab->check() if $data;
560 get
=> sub { return shift()->get(); }
567 options_dialog
([@opt_template, map {
569 label
=> $_, name
=> $_,
571 my ($parent, $data, @params) = @_;
572 my $lab=$parent->add(undef, "TextEntry", @params);
573 $lab->text($data) if defined $data;
576 get
=> sub { return shift()->get(); }
578 } LTPTests
::needed_vars
[grep { $_->{selected
} } @test_data]], \
%options);
583 my $selected_tests=[grep { $_->{selected
} } @test_data];
584 my @needed_vars=LTPTests
::needed_vars
$selected_tests;
585 my @unsatisfied_tests=LTPTests
::tests_needing_vars
$selected_tests, (grep !$options{$_}, @needed_vars);
586 if(@unsatisfied_tests) {
587 my $response=$cui->dialog(
588 -message
=> "You have sellected tests that need setting some variables. Would you like to set them now?",
590 {-label
=> "Show the options dialog", -value
=> 0},
591 {-label
=> "Don't run those tests", -value
=> 1},
592 {-label
=> "Cancel", -value
=> 2}
594 -title
=> "Unset variables"
596 return if($response == 2);
599 } else { # filter tests
600 $selected_tests=[grep { !any
{ !$options{$_} } LTPTests
::needed_vars
[$_] } @
$selected_tests ];
603 my @setups = LTPTests
::setup_scripts
$selected_tests;
604 $cui->leave_curses();
605 LTPTests
::execute_tests
($selected_tests, \
%options, ["clear; echo Running LTP Tests; ", map { "$_ setup;" } @setups], [map { "$_ teardown;" } @setups]);
606 $cui->reset_curses();
610 { -label
=> "Load", -value
=> sub {
611 my $file=$cui->loadfilebrowser();
612 LTPTests
::load_file
$file if defined $file;
613 $ts_list->values([sort keys (%testsuites)]);
615 { -label
=> "Execute", -value
=> \
&execute_tests
},
616 { -label
=> "Options", -value
=> \
&show_opt_dialog
},
617 { -label
=> "Exit", -value
=> sub { exit(0) } }
622 my $v=$ts_list->get_active_value();
623 $tc_list->values($testsuites{$v}->{tests
} );
624 my $seh=$tc_list->{-onchange
};
625 $tc_list->onChange(undef);
626 $tc_list->set_selection(grep { $tc_list->{-values}->[$_]->{selected
} } (0..$#{$tc_list->{-values}}));
627 $tc_list->onChange($seh);
628 $tc_list->intellidraw();
631 sub update_tc_selection
633 my @sel=$tc_list->get();
634 for(@
{$tc_list->{-values}}){
637 for(keys %{$_->{files
}}) {
638 $testsuites{$_}->{selected
}--;
640 $testsuites{"<ALL>"}->{selected
}--;
644 if(!$_->{selected
}) {
646 for(keys %{$_->{files
}}) {
647 $testsuites{$_}->{selected
}++;
649 $testsuites{"<ALL>"}->{selected
}++;
652 $ts_list->intellidraw();
658 my $seh=$tc_list->{-onchange
};
659 $tc_list->onChange(undef);
660 $tc_list->set_selection((0..$#{$tc_list->{-values}}));
661 $tc_list->onChange($seh);
663 $tc_list->intellidraw();
669 $tc_list->clear_selection();
670 update_tc_selection
(); # clear_selection doesn't call -onchange callback
671 $tc_list->intellidraw();
676 my $ts=$testsuites{$ts_list->get_active_value()};
677 if($ts->{count
}>$ts->{selected
}) {
686 my ($template, $options)=@_;
688 my $dialog=$cui->add(undef, "Window");
689 my $dialogid=$cui->{-object2id
}->{$dialog};
690 my $vbox = $dialog->add(undef, "Layout::Proportional",
692 my $opts_container = $vbox->add(undef, "Container::Paged", "-mult-height" => 1, -releasefocus
=> 1);
698 my $line = $opts_container->add(undef, "Layout::Proportional",
699 -vertical
=> 0, -height
=> 0,
701 my $label=$line->add(undef, "Label", -text
=> $_->{label
},
702 "-min-width" => $labelwidth,
704 push @widgets, $_->{create
}->($line, $options->{$_->{name
}}, "-mult-width" => 1);
707 $vbox->add(undef, "Buttonbox", "-min-height" => 3, -buttons
=>[
708 { -label
=> "Ok", -onpress
=> sub { $dialog->loose_focus; } }
709 ], -releasefocus
=> 1);
713 # run the dialog and wait for completion
717 $options->{$_->{name
}}=$_->{get
}->(shift(@widgets));
720 $cui->delete($dialogid);
721 $cui->root->focus(undef, 1);
724 $win = $cui->add("win", "Window", -padtop
=> 1);
728 -menu
=> [ { -label
=> "File", -submenu
=>$main_menu } ]
732 "layout", "Layout::Proportional", -vertical
=> 0, -border
=> 1
735 $ts_list=$cont->add (
736 "lb", "ListBox::MultiColumn",
737 -values => [sort keys (%testsuites)],
738 -vscrollbar
=> "right",
739 # -padtop => 2, -padleft => 2,
740 # -padright => 2, -padbottom => 2,
741 "-mult-width" => 1, -border
=> 1,
742 -onselchange
=> \
&update_tc_list
,
743 "-col-formatter" => sub {
744 my $tmp=$testsuites{$_[0]} // { selected
=> 0, count
=> 1 };
746 if($tmp->{selected
}==0) {
748 } elsif($tmp->{selected
}<$tmp->{count
}) {
758 $tc_list=$cont->add (
759 "lb2", "ListBox::MultiColumn",
760 -values => \
@test_data,
761 "-col-designator" => ["id", "command"],
762 -vscrollbar
=> "right",
763 # -padtop => 2, -padleft => 2,
764 # -padright => 2, -padbottom => 2,
765 "-mult-width" => 2, -border
=> 1,
767 -onchange
=> \
&update_tc_selection
770 $ts_list->set_routine('option-check', \
&check_ts
);
771 $ts_list->set_routine('option-uncheck', \
&uncheck_ts
);
772 $ts_list->set_routine('option-select', \
&toggle_ts
);
774 $cui->set_binding( sub{ $cui->focus('menu') }, Curses
::KEY_F
(10) );