#!@PERL@
# Featureful driver for test pattern generator
#
# Copyright 2006-2017 Robert Krawitz (rlk@alum.mit.edu) and other
# members of the Gutenprint project.
#
# 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; either version 2 of the License, or (at your option)
# any later version.
#
# 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, see .
use Getopt::Long;
Getopt::Long::Configure("bundling", "no_ignore_case", "pass_through");
use strict;
use POSIX ":sys_wait_h";
my $valgrind = 0;
my $callgrind = 0;
my $gdb_attach = 0;
my $compress_checksums = 0;
my $csum_file = undef;
my $csum_dir = undef;
my $csum_type = 'sha512';
my $dontrun = 0;
my $retval = 0;
my $halt_on_error = 0;
my $testpattern_command;
my @printer_list = ();
my @exclude_list = ();
my @include_patterns = ();
my @exclude_patterns = ();
my @special_options = ();
my $include_pattern = '';
my $exclude_pattern = '';
my %exclude_list = ();
my @standard_options = ("InkType",
"Resolution",
"InkSet,RawChannels",
"DitherAlgorithm",
"Duplex",
"MediaType",
"InputSlot",
"ColorCorrection",
"TPMode",
"ImageType",
"FullBleed",
"Quality",
"UseGloss",
"Weave",
"PrintingDirection",
"Density",
"CyanDensity",
"Orientation",
"LabelSeparator");
my @tp_modes = ("cmyk8",
"cmyk16",
"kcmy8",
"kcmy16",
"rgb8",
"rgb16",
"cmy8",
"cmy16",
"gray8",
"gray16",
"white8",
"white16");
my $global_status = 1;
my $run_installed = 0;
my $use_min_res = 0;
my $use_min_pagesize = 0;
my $help = 0;
my $output = undef;
my $hsize = 0.1;
my $vsize = 0.1;
my $left = 0.15;
my $top = 0.15;
my $geometry = "";
my $restart = 1;
my $global_fh;
my $error = 0;
my $global_printer;
my $global_messages;
my $global_page;
my $global_case;
my @extras = ();
my @job_extras = ();
my @messages = ();
my @global_messages = ();
my @families = ();
my %stpdata = ();
my %resolutions = ();
my %models_found = ();
my %models;
my %families;
my %defaults;
my $skip_duplicate_printers = 0;
my $std_pages = 1;
my $duplex_pages = 4;
my $skip_resolutions = 0;
my $quiet = 0;
my $single = 0;
my $rerun = 0;
my $list_printers = 0;
my $list_options = 0;
my $verbose = 0;
my $use_unused_options = 0;
my $proc_count = 1;
my $round = 0;
my $print_family = 0;
my $res_limit = 0;
my $proc_rotor = 0;
my $case_no = 0;
my $vg = "libtool --mode=execute valgrind";
my @default_options = ();
my %base_settings = ("DitherAlgorithm" => "Fast");
my %param_types;
my %stp_dimension_values;
my %stp_float_values;
my %stp_int_values;
my %min_page_size;
my %max_page_size;
my %stp_bools;
my %rerun_cases;
if (defined $ENV{"STP_PARALLEL"}) {
$proc_count = $ENV{"STP_PARALLEL"};
if ($proc_count > 1) {
$quiet = 1;
}
}
if (defined $ENV{"STP_TEST_FAMILY"}) {
@families = split(/ +/, $ENV{"STP_TEST_FAMILY"});
}
GetOptions("C=s" => \$csum_type,
"F" => \$print_family,
"G=s" => \$geometry,
"H!" => \$halt_on_error,
"L" => \$list_options,
"M:s" => \$csum_file,
"O=s" => \$output,
"P:i" => \$duplex_pages,
"R+" => \$use_min_res,
"S+" => \$single,
"T:i" => \$res_limit,
"V+" => \$verbose,
"X" => \$rerun,
"Y=s" => \@include_patterns,
"c" => \$callgrind,
"d=s" => \@default_options,
"f=s" => \@families,
"g" => \$gdb_attach,
"h" => \$help,
"i!" => \$run_installed,
"l" => \$list_printers,
"m:s" => \$csum_dir,
"n" => \$dontrun,
"o=s" => \@special_options,
"p:i" => \$std_pages,
"q+" => \$quiet,
"r!" => \$skip_resolutions,
"s!" => \$skip_duplicate_printers,
"N!" => \$use_unused_options,
"v+" => \$valgrind,
"x=s" => \@exclude_list,
"y=s" => \@exclude_patterns,
"u" => \$round,
"t:i" => \$proc_count);
sub print_help_and_exit() {
my $options = join("\n ", sort @standard_options);
print STDERR < $b } keys %ropt;
} else {
@special_options = @standard_options;
}
my $bad_opt = 0;
foreach my $opt (@default_options) {
if (! ($opt =~ /=/)) {
print STDERR "Malformed default option `$opt'\n";
$bad_opt = 1;
}
my ($option, $value) = split(/=/, $opt);
if (! $value) {
delete $base_settings{$option};
} else {
$base_settings{$option} = $value;
}
}
if ($bad_opt) {
print_help_and_exit();
}
my $pwd = `pwd`;
chomp $pwd;
my $srcdir = $ENV{"srcdir"};
my $sdir;
$geometry =~ s/^=*//;
if ($geometry =~ /^(([01]?(\.[0-9]*)?)(x([01]?(\.[0-9]*)?))?)?(\+([01]?(\.[0-9]*)?)(\+([01]?(\.[0-9]*)?))?)?$/) {
my ($H) = $2;
my ($V) = $5;
my ($L) = $8;
my ($T) = $11;
if ($H) {
$hsize = $H;
if (not $V) {
$V=$H;
}
if ($hsize > 1) {
$hsize = 1;
}
}
if ($V) {
$vsize = $V;
if ($vsize > 1) {
$vsize = 1;
}
}
if ($L) {
if (not $T) {
$T=$L;
}
$left = $L;
}
if ($T) {
$top = $T;
}
if ($left + $hsize > 1) {
$left = 1 - $hsize;
}
if ($top + $vsize > 1) {
$top = 1 - $vsize;
}
}
if ("$srcdir" eq "" || "$srcdir" eq ".") {
$sdir = $pwd;
} elsif ($srcdir =~ /^\//) {
$sdir = "$srcdir";
} else {
$sdir = "$pwd/$srcdir";
}
if (! $run_installed && ! defined $ENV{"STP_DATA_PATH"}) {
$ENV{"STP_DATA_PATH"} = "${sdir}/../xml";
}
if (! defined $ENV{"STP_MODULE_PATH"}) {
$ENV{"STP_MODULE_PATH"} = "${sdir}/../main:${sdir}/../main/.libs";
}
sub set_opt($$$) {
my ($opt, $val, $printer) = @_;
my ($type) = $param_types{$printer}{$opt};
if ($type == 1) {
push @extras, "parameter_int \"$opt\" $val;\n";
} elsif ($type == 2) {
my ($xval) = $val;
if ($val =~ /true/i) {
$xval = 1;
} elsif ($val =~ /false/i) {
$xval = 0;
}
push @extras, "parameter_bool \"$opt\" $xval;\n";
} elsif ($type == 3) {
push @extras, "parameter_float \"$opt\" $val;\n";
} elsif ($type == 4) {
push @extras, "parameter_curve \"$opt\" \"$val\";\n";
} else {
if ($opt eq "PageSize" && $val =~ /^([^.]+)\.([0-9]+)x([0-9]+)$/) {
push @extras, "parameter \"PageSize\" \"$1\";\n";
push @extras, "page_size $2 $3;\n";
} else {
push @extras, "parameter \"$opt\" \"$val\";\n";
}
}
}
sub set_message($) {
my ($message) = @_;
push @messages, "message \"$message\";\n" if (! $quiet);
}
sub set_global_message($) {
my ($message) = @_;
push @global_messages, "message \"$message\";\n"
}
sub print_one_testpattern($$$;$) {
my ($printer, $pages, $tp_mode, $raw) = @_;
my $stuff = join "", @global_messages;
$stuff .= join "", @job_extras;
$global_printer = $printer;
foreach my $page (0..$pages - 1) {
$global_page = $page;
$stuff .= "printer \"$printer\";\n";
$stuff .= "parameter \"PageSize\" \"Auto\";\n";
$stuff .= join "", @messages, @extras;
if (! $quiet && $pages > 1) {
$stuff .= "message \"(page $page)\";\n";
}
$stuff .= "parameter_int \"PageNumber\" $page;\n";
if ($page == 0) {
$stuff .= "start_job;\n";
}
if ($page == $pages - 1) {
$stuff .= "end_job;\n";
}
$stuff .= sprintf("hsize %f;\n", $hsize);
$stuff .= sprintf("vsize %f;\n", $vsize);
$stuff .= sprintf("left %f;\n", $left);
$stuff .= sprintf("top %f;\n", $top);
$stuff .= "blackline 0;\n";
$stuff .= "steps 16;\n";
if ($round) {
$stuff .= "round;\n";
}
if ($raw > 0) {
$stuff .= "mode extended $raw 16;\n";
$stuff .= "xpattern ";
for (my $i = 0; $i < $raw; $i++) {
$stuff .= "0.0 0.0 1.0 ";
}
$stuff .= ";\n";
for (my $i = 0; $i < $raw; $i++) {
$stuff .= "xpattern ";
for (my $j = 0; $j < $raw; $j++) {
if ($i == $j) {
$stuff .= "0.0 1.0 1.0 ";
} else {
$stuff .= "0.0 0.0 1.0 ";
}
}
$stuff .= ";\n";
}
$stuff .= "xpattern ";
for (my $i = 0; $i < $raw; $i++) {
$stuff .= "0.0 0.0 1.0 ";
}
$stuff .= ";\n";
$stuff .= "end;\n";
} else {
$tp_mode =~ s/([81]+)/ $1/;
$stuff .= << "EOF";
mode $tp_mode;
pattern 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 ;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 -2.0 1.0 0.0 -2.0 1.0 0.0 -2.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 1.0 1.0 -2.0 -2.0 -2.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.1 0.3 1.0 1.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.3 0.7 -2.0 -2.0 -2.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.1 0.999 1.0 1.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.3 0.999 1.0 1.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.5 0.999 1.0 1.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.1 0.3 -2.0 -2.0 -2.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.3 0.7 1.0 1.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.1 0.999 -2.0 -2.0 -2.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.3 0.999 -2.0 -2.0 -2.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 0.5 0.999 -2.0 -2.0 -2.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.25 1.0 0.0 0.0 1.0 0.0 0.75 1.0 0.0 0.75 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.25 1.0 0.0 0.0 1.0 0.0 0.75 1.0 0.0 0.75 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.5 1.0 0.0 0.0 1.0 0.0 0.5 1.0 0.0 0.5 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.5 1.0 0.0 0.0 1.0 0.0 0.5 1.0 0.0 0.5 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.75 1.0 0.0 0.0 1.0 0.0 0.25 1.0 0.0 0.25 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.75 1.0 0.0 0.0 1.0 0.0 0.25 1.0 0.0 0.25 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.9 1.0 0.0 0.0 1.0 0.0 0.1 1.0 0.0 0.1 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.9 1.0 0.0 0.0 1.0 0.0 0.1 1.0 0.0 0.1 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.25 1.0 0.0 0.75 1.0 0.0 0.0 1.0 0.0 0.75 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.25 1.0 0.0 0.75 1.0 0.0 0.0 1.0 0.0 0.75 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.0 1.0 0.0 0.5 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.0 1.0 0.0 0.5 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.75 1.0 0.0 0.25 1.0 0.0 0.0 1.0 0.0 0.25 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.75 1.0 0.0 0.25 1.0 0.0 0.0 1.0 0.0 0.25 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.9 1.0 0.0 0.1 1.0 0.0 0.0 1.0 0.0 0.1 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.9 1.0 0.0 0.1 1.0 0.0 0.0 1.0 0.0 0.1 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.25 1.0 0.0 0.75 1.0 0.0 0.75 1.0 0.0 0.0 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.25 1.0 0.0 0.75 1.0 0.0 0.75 1.0 0.0 0.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.0 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.75 1.0 0.0 0.25 1.0 0.0 0.25 1.0 0.0 0.0 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.75 1.0 0.0 0.25 1.0 0.0 0.25 1.0 0.0 0.0 1.0;
pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.9 1.0 0.0 0.1 1.0 0.0 0.1 1.0 0.0 0.0 1.0;
pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.9 1.0 0.0 0.1 1.0 0.0 0.1 1.0 0.0 0.0 1.0;
end;
EOF
}
}
return $stuff;
}
sub get_printers() {
map { $exclude_list{$_} = 1; } @exclude_list;
$include_pattern = join("|", map {"($_)"} @include_patterns);
$exclude_pattern = join("|", map {"($_)"} @exclude_patterns);
my (@nprinter_list);
my $extra_arg = "";
my (%families);
my (%model_families);
my (%printer_models);
map { $families{$_} = 1; } @families;
open PIPE, "./printers|" or die "Cannot run printers: $!\n";
my ($line_count);
while() {
next if m!^#!;
chomp;
$line_count++;
my ($model, $family, $printer) = split;
my ($model_family) = "${model}_${family}";
$printer_models{$printer} = $model_family;
if (! defined $model_families{$model_family}) {
$model_families{$model_family} = $printer;
}
next if @families && ! defined $families{$family};
next if $exclude_list{$printer};
next if $exclude_pattern ne '' && $printer =~ /$exclude_pattern/;
next if $include_pattern ne '' && ! $printer =~ /$include_pattern/;
next if $skip_duplicate_printers && $model_families{$model_family} ne $printer;
push @nprinter_list, $printer;
}
close PIPE;
if ($#ARGV >= 0) {
my ($bad_printers_found) = 0;
@nprinter_list = grep { defined $_ } map {
if (defined $printer_models{$_}) {
$_;
} elsif (defined $model_families{$_}) {
$model_families{$_};
} else {
print STDERR "Unknown printer ${_}!\n";
$bad_printers_found = 1;
undef;
}
} @ARGV;
}
exit 1 if (! scalar @nprinter_list);
return @nprinter_list;
}
sub load_printer_data() {
my ($extra_arg) = join(" ", @printer_list);
open PIPE, "./printer_options $extra_arg|" or die "Cannot run printer_options: $!\n";
while() {
next if m!^#!;
eval $_;
}
close PIPE or die "Cannot run printer_options: $!\n";
if ($rerun) {
while (<>) {
chomp;
s/^[^a-zA-Z]*//;
s/\..*//;
$rerun_cases{$_} = 1;
}
}
my (@nprinter_list);
foreach my $printer (@printer_list) {
my $model_id = $models{$printer};
my $family_id = $families{$printer};
if (($skip_duplicate_printers && $models_found{$family_id}{$model_id}) ||
(@families && ! grep { $family_id eq $_ } @families)) {
} else {
$models_found{$family_id}{$model_id} = 1;
push @nprinter_list, $printer;
}
}
@printer_list = @nprinter_list;
}
sub do_print($$) {
my ($output, $fh) = @_;
if ($dontrun) {
print $output;
} elsif (! $fh) {
restart_testpattern(1);
print $global_fh $output;
my ($status) = close $global_fh;
if (!$status) {
if ($? == 2) {
stopit();
}
$error++;
if ($single > 1) {
warn("\n*** FAIL $global_case ($?)\n");
} else {
warn("\n*** FAIL ($?)\n");
}
if ($halt_on_error) { # SIGINT
stopit();
}
}
} else {
print $fh $output;
}
}
sub do_output($) {
my ($outkey) = @_;
if (defined $output || defined $csum_dir ||
defined $csum_file) {
my $checksum_cmd;
my $outbase = "${outkey}.prn";
my $outfile = "$output/$outbase";
if (defined $csum_file) {
$checksum_cmd = "${csum_type}sum -b | sed 's/-/$outbase/'";
if ($csum_file =~ /^\:([0-9]+)$/) {
$checksum_cmd .= " 1>&$1";
} elsif ($csum_file ne '' && $csum_file ne '-') {
$checksum_cmd .= " >> '$csum_file'";
}
} elsif (defined $csum_dir) {
my $checksum_dir = $csum_dir;
if (defined $csum_dir && $csum_dir eq "") {
$checksum_dir = $output;
}
my $checksumfile = "$checksum_dir/${outkey}.checksum";
if (! $checksum_dir) {
$checksumfile = "${outkey}.checksum";
}
$checksum_cmd = "${csum_type}sum -b | sed 's/-/$outbase/' > '$checksumfile'";
}
if ($output && $checksum_cmd ne "") {
push @job_extras, "output \"|tee '$outfile' | $checksum_cmd\";\n";
} elsif ($output) {
push @job_extras, "output \"$outfile\";\n";
} else {
push @job_extras, "output \"|$checksum_cmd\";\n";
}
}
}
sub build_list($$$$) {
my ($keys, $printer, $min_res, $max_res) = @_;
my (@keys) = split (/,/, $keys);
my ($key) = $keys[0];
my ($rest);
my (@stuff);
if ($#keys > 0) {
$rest = join ",", @keys[1..$#keys];
@stuff = build_list($rest, $printer, $min_res, $max_res);
}
my (@tmp);
if ($key =~ /=/) {
my (@values);
my ($vlist);
($key, $vlist) = split(/=/, $key);
@tmp = split(/;/, $vlist);
if ($key eq 'Resolution') {
@tmp = map {
if ($_ eq 'MIN') {
$min_res;
} elsif ($_ eq 'MAX') {
$max_res;
} else {
$_
}
} @tmp;
} elsif ($key eq 'PageSize') {
@tmp = map {
if ($_ eq 'MIN') {
$min_page_size{$printer}
} elsif ($_ eq 'MAX') {
$max_page_size{$printer}
} else {
$_
}
} @tmp;
}
} elsif ($param_types{$printer}{$key} == 3) {
my (@vals) = @{$stp_float_values{$printer}{$key}};
my ($minv) = $vals[2];
my ($defv) = $vals[1];
my ($maxv) = $vals[3];
push @tmp, $minv;
push @tmp, ($minv + $defv) / 2.0;
push @tmp, $defv;
push @tmp, ($defv + $maxv) / 2.0;
push @tmp, $maxv;
} elsif ($param_types{$printer}{$key} == 1) {
my (@vals) = @{$stp_int_values{$printer}{$key}};
my ($minv) = $vals[2];
my ($maxv) = $vals[3];
push @tmp, ($minv..$maxv);
} elsif ($param_types{$printer}{$key} == 2) {
push @tmp, 0;
push @tmp, 1;
} elsif ($key eq "TPMode") {
push @tmp, @tp_modes;
} elsif ($key eq "Resolution") {
@tmp = sort keys %{$resolutions{$printer}};
if ($res_limit > 0) {
@tmp = grep { $resolutions{$printer}{$_}[0] <= $res_limit &&
$resolutions{$printer}{$_}[1] <= $res_limit} @tmp;
}
if (! @tmp) {
push @tmp, $min_res;
}
} elsif (defined($param_types{$printer}{$key})) {
@tmp = @{$stpdata{$printer}{$key}};
}
if (! @tmp) {
return @stuff;
} elsif (! @stuff) {
return @tmp;
} else {
my (@answer);
foreach my $i (@tmp) {
foreach my $j (@stuff) {
push @answer, "$i,$j";
}
}
return @answer;
}
return @tmp;
}
sub build_key($$) {
my ($keys, $printer) = @_;
my (@keys) = split (/,/, $keys);
my (@answer) = grep { $_ eq "TPMode" || defined $param_types{$printer}{$_} || /=/ } @keys;
return join ",", @answer;
}
sub get_min_max_res($) {
my ($printer) = @_;
my (%reslist) = %{$resolutions{$printer}};
my $min_res_name;
my $min_res_value = 5760*5760;
my $max_res_name;
my $max_res_value = 0;
# We want to get the same named resolution each time.
foreach my $resid (sort keys %reslist) {
my ($res) = $reslist{$resid};
my ($res_value) = $$res[0] * $$res[1];
if ($res_value < $min_res_value) {
$min_res_value = $res_value;
$min_res_name = $resid;
}
if ($res_value > $max_res_value) {
$max_res_value = $res_value;
$max_res_name = $resid;
}
}
return ($min_res_name, $max_res_name);
}
sub get_printing_modes($) {
my ($printer) = @_;
my (@printing_modes) = grep {$_ ne 'None' } @{$stpdata{$printer}{'PrintingMode'}};
if ($base_settings{'PrintingMode'}) {
if ($base_settings{'PrintingMode'} eq 'Color' &&
grep { $_ eq 'Color' } @printing_modes) {
return ('Color');
} elsif ($base_settings{'PrintingMode'} eq 'BW' &&
grep { $_ eq 'BW' } @printing_modes) {
return ('BW');
} else {
return ();
}
} else {
return @printing_modes;
}
}
sub get_optvals($$$) {
my ($printer, $min_res_name, $max_res_name) = @_;
my (%opt_vals);
if (! @special_options) {
$opt_vals{"Default"} = ['all'];
} else {
foreach my $key (@special_options) {
my (@subkeys) = (split(/,/, $key));
my $nkey = build_key($key, $printer);
if ($nkey ne "") {
my (@vals) = build_list($nkey, $printer, $min_res_name, $max_res_name);
$nkey =~ s/=[^,]*//g;
$opt_vals{$nkey} = \@vals;
}
}
}
return %opt_vals;
}
sub setup_printer_params($) {
my ($printer) = @_;
if ($stpdata{$printer}) {
foreach my $k (sort keys %{$stpdata{$printer}}) {
my ($vals) = $stpdata{$printer}{$k};
foreach my $v (0..@$vals) {
if (substr($$vals[$v], 0, 1) eq '+') {
$$vals[$v] = substr($$vals[$v], 1);
$defaults{$printer}{$k} = $$vals[$v];
last;
}
}
}
map { $param_types{$printer}{$_} = 0 } keys %{$stpdata{$printer}};
}
$param_types{$printer}{"Resolution"} = 0;
if ($stp_int_values{$printer}) {
map { $param_types{$printer}{$_} = 1 } keys %{$stp_int_values{$printer}};
}
if ($stp_bools{$printer}) {
map { $param_types{$printer}{$_} = 2 } keys %{$stp_bools{$printer}};
}
if ($stp_float_values{$printer}) {
map { $param_types{$printer}{$_} = 3 } keys %{$stp_float_values{$printer}};
}
if ($stp_dimension_values{$printer}) {
map { $param_types{$printer}{$_} = 8 } keys %{$stp_dimension_values{$printer}};
}
# curve = 4
# file = 5
# raw = 6
# array = 7
}
# Resolution and PageSize need special handling because of MIN and MAX
# Duplex needs special handling because of printing multiple pages, and
# need to set JobMode
# RawChannels needs special handling because it's handled specially by
# the underlying testpattern command.
sub do_printer($$) {
my ($printer, $global_fh) = @_;
my $first_time = 1;
my %opt_vals = {};
setup_printer_params($printer);
my (@printing_modes) = get_printing_modes($printer);
return if (! @printing_modes);
my ($min_res, $max_res) = get_min_max_res($printer);
my (%opt_vals) = get_optvals($printer, $min_res, $max_res);
foreach my $pmode (sort @printing_modes) {
foreach my $key (sort keys %opt_vals) {
next if ($key eq "RawChannels" && $pmode ne "Color");
next if (! defined $opt_vals{$key} || $opt_vals{$key} < 1);
my (@opts) = sort @{$opt_vals{$key}};
my (@subkeys) = split (/,/, $key);
my (%subkeys);
map {$subkeys{$_} = 1} @subkeys;
my $opt;
foreach $opt (@opts) {
@messages = ();
@global_messages = ();
my ($pname) = $print_family ?
"${models{$printer}}-$families{$printer}" : $printer;
if ($first_time) {
if ($quiet < 2) {
set_global_message("$pname\n");
} elsif ($quiet == 2) {
set_global_message(".");
}
$first_time = 0;
}
next if (($case_no++ % $proc_count) != $proc_rotor);
my ($tp_mode) = "rgb8";
@extras = ();
@job_extras = ();
my (@ovals);
my $rawval;
my (@subopts) = split (/,/, $opt);
map {
my $k = $subkeys[$_];
my $v = $subopts[$_];
next if ($k eq "RawChannels" && $v ne "None" && $pmode ne "Color");
if ($k eq "RawChannels") {
$rawval = $v;
} elsif ($k eq "TPMode") {
$tp_mode = $v;
} else {
set_opt($k, $v, $printer);
}
push @ovals, "${k}=${v}";
} (0..$#subkeys);
map {
if ($key ne $_) {
my $setting = $base_settings{$_};
if ($_ eq "Resolution" && $setting eq "MIN") {
$setting = $min_res;
} elsif ($_ eq "Resolution" && $setting eq "MAX") {
$setting = $max_res;
} elsif ($_ eq "PageSize" && $setting eq "MIN") {
$setting = $min_page_size{$printer};
} elsif ($_ eq "PageSize" && $setting eq "MAX") {
$setting = $max_page_size{$printer};
}
set_opt($_, $setting, $printer);
push @ovals, "${_}=$setting";
}
} grep { ! defined $subkeys{$_} &&
($use_unused_options || $_ eq "Resolution" ||
$_ eq "TPMode" ||
defined $stpdata{$printer}{$_})} (sort keys %base_settings);
my $pages = $std_pages;
set_opt("PrintingMode", $pmode, $printer);
if ($key =~ /Duplex/) {
set_opt("JobMode", "Job", $printer);
$pages = $duplex_pages;
}
my $rawval;
my ($mstring) = join "_", @ovals;
$mstring =~ s/=/_/g;
my ($ostring) = join "+", @ovals;
my $case = "${pname}_PrintingMode_${pmode}_${mstring}";
$global_case = $case;
if (! $rerun || $rerun_cases{$case}) {
$global_messages = $case;
do_output($case);
set_message(" ${pmode}+${ostring}");
my $output = print_one_testpattern($printer, $pages, $tp_mode, $rawval);
do_print($output, $global_fh);
}
}
}
}
# Increment the rotor here also, so that if the degree of parallelism is even
# and we're running with -o - (an easy way to run minimal cases) we won't wind
# up with half the workers running color and half running black and white.
$case_no++;
}
sub restart_testpattern {
my ($silent) = @_;
if (! $silent) {
close($global_fh);
my ($err) = $? & 255;
if ($err > 0) {
$error++;
}
if ($single > 1) {
warn("\n***TESTPATTERN CRASHED*** ($global_case) ($err)!\n");
} else {
warn("\n***TESTPATTERN CRASHED*** (cannot determine case, near $global_case) ($err)!\n");
}
if ($halt_on_error) {
stopit();
}
}
open($global_fh, "|$testpattern_command") or
die "Can't run $testpattern_command: $!\n";
}
sub stopit {
if ($single > 1) {
print "\nStopping, $error case(s) failed.\n";
exit 1;
} elsif ($error) {
print "\nStopping, failures noted.\n";
exit 1;
} else {
print "\nStopping.\n";
exit 0;
}
}
sub get_default($$) {
my ($printer, $opt) = @_;
if (defined $defaults{$printer}{$opt}) {
return $defaults{$printer}{$opt};
} else {
return $stpdata{$printer}{$opt}[0];
}
}
sub list_options() {
if ($#printer_list > 0) {
die "-L must be used with one printer\n";
}
my ($printer) = $printer_list[0];
foreach my $opt (sort keys %{$stpdata{$printer}}) {
print "$opt\n";
if ($verbose) {
if ($param_types{$printer}{$opt} == 3) {
my (@vals) = @{$stp_float_values{$printer}{$opt}};
print " $vals[2]\n";
print " =>$vals[1]\n";
print " $vals[3]\n";
} elsif ($param_types{$printer}{$opt} == 1) {
my (@vals) = @{$stp_float_values{$printer}{$opt}};
print " $vals[2]\n";
print " $vals[3]\n";
} elsif ($param_types{$printer}{$opt} == 2) {
if ($defaults{$printer}{$opt}) {
print " 0\n";
print " =>1\n";
} else {
print " =>0\n";
print " 1\n";
}
} else {
my ($default) = get_default($printer, $opt);
foreach my $val (sort @{$stpdata{$printer}{$opt}}) {
if ($val eq $default) {
print " =>$val\n";
} else {
print " $val\n";
}
}
}
}
}
}
sub runit() {
my $valgrind_command;
my $valopts;
if ($gdb_attach) {
$proc_count = 1;
}
# V A L G R I N D #
if ($callgrind) {
$valopts = '--tool=callgrind --dump-instr=yes --trace-jump=yes';
$valgrind = 4;
} elsif ($valgrind) {
$valopts = '--tool=memcheck';
}
if ($gdb_attach) {
$valopts .= ' --vgdb=yes';
}
if ($valgrind == 1) {
$valgrind_command = "$vg $valopts -q --num-callers=50 --error-limit=no --leak-check=yes --error-exitcode=1";
} elsif ($valgrind == 2) {
$valgrind_command = "$vg $valopts --num-callers=50 --error-limit=no --leak-resolution=high --leak-check=yes --error-exitcode=1";
} elsif ($valgrind == 3) {
$valgrind_command = "$vg $valopts --error-limit=no --num-callers=50 --show-reachable=yes --leak-resolution=high --leak-check=yes --error-exitcode=1";
} elsif ($valgrind == 4) {
$valgrind_command = "$vg $valopts --error-exitcode=1";
}
# O U T P U T #
if (defined $output && $output ne "" && ! -d $output) {
mkdir $output || die "Can't create directory $output: $!\n";
}
if (defined $csum_dir && $csum_dir ne "" && ! -d $csum_dir) {
mkdir $csum_dir || die "Can't create directory $csum_dir: $!\n";
}
if (defined $csum_file && ! $csum_file =~ /^\:([0-9]+)$/) {
unlink $csum_file;
system "touch $csum_file";
}
# # # # # # # #
my ($suppress);
if (! defined $output && ! defined $csum_dir &&
! defined $csum_file) {
$suppress = '-n';
}
my (@children);
my ($child_no);
my ($kid);
load_printer_data();
# Note that when testing duplex all pages of the duplex run are done by
# one process, but each page counts as a separate test. So the number
# of cases per process may not always be within 1.
if ($proc_count > 1) {
for ($child_no = 0; $child_no < $proc_count; $child_no++) {
$kid = fork();
if ($kid == 0) {
last;
} else {
push @children, $kid;
$proc_rotor++;
}
}
}
my $status = 1;
if ($proc_count > 1 && $kid > 0) {
# Parent in parallel case
while ($proc_count > 0 && $kid > 0) {
$kid = waitpid(-1, 0);
if ($kid > 0 && $? > 0) {
$error++;
}
}
} else {
my ($qopt) = $quiet ? "-q" : "";
my ($Hopt) = $halt_on_error ? "-H" : "";
$testpattern_command = "$valgrind_command ./testpattern -y $suppress $qopt $Hopt";
if ($single > 1) {
$SIG{TERM} = sub() { stopit() };
$SIG{HUP} = sub() { stopit() };
$SIG{INT} = sub() { stopit() };
foreach my $printer (@printer_list) {
do_printer($printer, undef);
}
} elsif ($single) {
$SIG{PIPE} = sub() { restart_testpattern() };
foreach my $printer (@printer_list) {
restart_testpattern(1);
do_printer($printer, $global_fh);
$status |= close $global_fh;
$error += ($? != 0);
}
} else {
$SIG{PIPE} = sub() { restart_testpattern() };
restart_testpattern(1);
map { do_printer($_, $global_fh) } @printer_list;
$status = close $global_fh;
}
}
if ($quiet == 2) {
print STDERR "\n";
}
if ($status && ! $error) {
return 0;
} else {
if ($error) {
if ($single > 1) {
print STDERR "$error cases failed\n";
} else {
my ($plural) = ($error > 1 ? "ES" : "");
print STDERR "*** $error CRASH${plural} NOTED***\n";
}
}
return 1;
}
}
@printer_list = get_printers();
if ($list_printers) {
print join("\n", sort @printer_list), "\n";
} elsif ($list_options) {
load_printer_data();
list_options();
} elsif ($dontrun) {
load_printer_data();
map { do_printer($_, \*STDOUT) } @printer_list;
} else {
exit(runit());
}