#!/usr/bin/perl # # xrandr Test suite # # Do a set of xrandr calls and verify that the screen setup is as expected # after each call. # $xrandr="xrandr"; $xrandr=$ENV{XRANDR} if defined $ENV{XRANDR}; $version="0.1"; $inbetween=""; print "\n***** xrandr test suite V$version *****\n\n"; # Known issues and their fixes %fixes=( s2 => "xrandr: 307f3686", s4 => "xserver: f7dd0c72", s11 => "xrandr: f7aaf894", s18 => "issue known, but not fixed yet" ); # Get output configuration @outputs=(); %mode_name=(); %out_modes=(); %modes=(); open P, "$xrandr --verbose|" or die "$xrandr"; while (

) { if (/^\S/) { $o=""; $m=""; $x=""; } if (/^(\S+)\s(connected|unknown connection)\s/) { $o=$1; push @outputs, $o if $2 eq "connected"; push @outputs_unknown, $o if $2 eq "unknown connection"; $out_modes{$o}=[]; } elsif (/^\s+(\d+x\d+)\s+\((0x[0-9a-f]+)\)/) { my $m=$1; my $x=$2; while (

) { if (/^\s+(\d+x\d+)\s+\((0x[0-9a-f]+)\)/) { print "WARNING: Ignoring incomplete mode $x:$m on $o\n"; $m=$1, $x=$2; } elsif (/^\s+v:.*?([0-9.]+)Hz\s*$/) { if (defined $mode_name{$x} && $mode_name{$x} ne "$m\@$1") { print "WARNING: Ignoring mode $x:$m\@$1 because $x:$mode_name{$x} already exists\n"; last; } if (defined $modes{"$o:$x"}) { print "WARNING: Ignoring duplicate mode $x on $o\n"; last; } $mode_name{$x}="$m\@$1"; push @{$out_modes{$o}}, $x; $modes{"$o:$x"}=$x; $modes{"$o:$m\@$1"}=$x; $modes{"$o:$m"}=$x; last; } } } } close P; @outputs=(@outputs,@outputs_unknown) if @outputs < 2; # preamble if ($ARGV[0] eq "-w") { print "Waiting for keypress after each test for manual verification.\n\n"; $inbetween='print " Press to continue...\n"; $_='; } elsif ($ARGV[0] ne "") { print "Preparing for test # $ARGV[0]\n\n"; $prepare = $ARGV[0]; } print "Detected connected outputs and available modes:\n\n"; for $o (@outputs) { print "$o:"; my $i=0; for $x (@{$out_modes{$o}}) { print "\n" if $i++ % 3 == 0; print " $x:$mode_name{$x}"; } print "\n"; } print "\n"; if (@outputs < 2) { print "Found less than two connected outputs. No tests available for that.\n"; exit 1; } if (@outputs > 2) { print "Note: No tests for more than two connected outputs available yet.\n"; print "Using the first two outputs.\n\n"; } $a=$outputs[0]; $b=$outputs[1]; # For each resolution only a single refresh rate should be used in order to # reduce ambiguities. For that we need to find unused modes. The %used hash is # used to track used ones. All references point to . # : # :x@ # :x # # x@ # x %used=(); # Find biggest common mode undef $sab; for my $x (@{$out_modes{$a}}) { if (defined $modes{"$b:$x"}) { $m=$mode_name{$x}; $sab="$x:$m"; $m =~ m/(\d+x\d+)\@([0-9.]+)/; $used{$x} = $x; $used{$1} = $x; $used{"$a:$x"} = $x; $used{"$b:$x"} = $x; $used{"$a:$m"} = $mode_name{$x}; $used{"$b:$m"} = $mode_name{$x}; $used{"$a:$1"} = $x; $used{"$b:$1"} = $x; last; } } if (! defined $sab) { print "Cannot find common mode between $a and $b.\n"; print "Test suite is designed to need a common mode.\n"; exit 1; } # Find sets of additional non-common modes # Try to get non-overlapping resolution set, but if that fails get overlapping # ones but with different refresh values, if that fails any with nonequal # timings, and if that fails any one, but warn. # Try modes unknown to other outputs first, they might need common ones # themselves. sub get_mode { my $o=$_[0]; for my $pass (1, 2, 3, 4, 5, 6, 7, 8, 9) { CONT: for my $x (@{$out_modes{$o}}) { $m = $mode_name{$x}; $m =~ m/(\d+x\d+)\@([0-9.]+)/; next CONT if defined $used{"$o:$x"}; next CONT if $pass < 9 && defined $used{"$o:$m"}; next CONT if $pass < 7 && defined $used{"$o:$1"}; next CONT if $pass < 6 && defined $used{$m}; next CONT if $pass < 4 && defined $used{$1}; for my $other (@outputs) { next if $other eq $o; next CONT if $pass < 8 && defined $used{"$o:$x"}; next CONT if $pass < 5 && $used{"$other:$1"}; next CONT if $pass < 3 && $modes{"$other:$m"}; next CONT if $pass < 2 && $modes{"$other:$1"}; } if ($pass >= 6) { print "Warning: No more non-common modes, using $m for $o\n"; } $used{"$o:$x"} = $x; $used{"$o:$m"} = $x; $used{"$o:$1"} = $x; $used{$x} = $x; $used{$m} = $x; $used{$1} = $x; return "$x:$m"; } } print "Warning: Cannot find any more modes for $o.\n"; return undef; } sub mode_to_randr { $_[0] =~ m/^(0x[0-9a-f]+):(\d+)x(\d+)\@([0-9.]+)/; return "--mode $1"; } $sa1=get_mode($a); $sa2=get_mode($a); $sb1=get_mode($b); $sb2=get_mode($b); $mab=mode_to_randr($sab); $ma1=mode_to_randr($sa1); $ma2=mode_to_randr($sa2); $mb1=mode_to_randr($sb1); $mb2=mode_to_randr($sb2); # Shortcuts $oa="--output $a"; $ob="--output $b"; # Print config print "A: $a (mab,ma1,ma2)\nB: $b (mab,mb1,mb2)\n\n"; print "mab: $sab\nma1: $sa1\nma2: $sa2\nmb1: $sb1\nmb2: $sb2\n\n"; print "Initial config:\n"; system "$xrandr"; print "\n"; # Test subroutine sub t { my $name=$_[0]; my $expect=$_[1]; my $args=$_[2]; print "*** $name: $args\n"; print "? $expect\n" if $expect ne ""; if ($name eq $prepare) { print "-> Prepared to run test\n\nRun test now with\n$xrandr --verbose $args\n\n"; exit 0; } my %r = (); my $r = ""; my $out = ""; if (system ("$xrandr --verbose $args") == 0) { # Determine active configuration open P, "$xrandr --verbose|" or die "$xrandr"; my ($o, $c, $m, $x); while (

) { $out.=$_; if (/^\S/) { $o=""; $c=""; $m=""; $x=""; } if (/^(\S+)\s(connected|unknown connection) (\d+x\d+)\+\d+\+\d+\s+\((0x[0-9a-f]+)\)/) { $o=$1; $m=$3; $x=$4; $o="A" if $o eq $a; $o="B" if $o eq $b; } elsif (/^\s*CRTC:\s*(\d)/) { $c=$1; } elsif (/^\s+$m\s+\($x\)/) { while (

) { $out.=$_; if (/^\s+\d+x\d+\s/) { $r{$o}="$x:$m\@?($c)" unless defined $r{$o}; # we don't have to reparse this - something is wrong anyway, # and it probably is no relevant resolution as well last; } elsif (/^\s+v:.*?([0-9.]+)Hz\s*$/) { $r{$o}="$x:$m\@$1($c)"; last; } } } } for $o (sort keys %r) { $r .= " $o: $r{$o}"; } close P; } else { $expect="success" if $expect=""; $r="failed"; } # Verify if ($expect ne "") { print "->$r\n"; if ($r eq " $expect") { print "-> ok\n\n"; } else { print "\n$out"; print "\n-> FAILED: Test # $name:\n\n"; print " $xrandr --verbose $args\n\n"; if ($fixes{$name}) { print "\nThere are known issues with some packages regarding this test.\n"; print "Please verify that you have at least the following git versions\n"; print "before reporting a bug to xorg-devel:\n\n"; print " $fixes{$name}\n\n"; } exit 1; } eval $inbetween; } else { print "-> ignored\n\n"; } } # Test cases # # The tests are carefully designed to test certain transitions between # RandR states that can only be reached by certain calling sequences. # So be careful with altering them. For additional tests, better add them # to the end of already existing tests of one part. # Part 1: Single output switching tests (except for trivial explicit --crtc) t ("p", "", "$oa --off $ob --off"); t ("s1", "A: $sa1(0)", "$oa $ma1 --crtc 0"); t ("s2", "A: $sa1(0) B: $sab(1)", "$ob $mab"); # TODO: should be A: $sab(1) someday (auto re-cloning)" #t ("s3", "A: $sab(1) B: $sab(1)", "$oa $mab"); t ("s3", "A: $sab(0) B: $sab(1)", "$oa $mab --crtc 0"); t ("p4", "A: $sab(1) B: $sab(1)", "$oa $mab --crtc 1 $ob --crtc 1"); t ("s4", "A: $sa2(0) B: $sab(1)", "$oa $ma2"); t ("s5", "A: $sa1(0) B: $sab(1)", "$oa $ma1"); t ("s6", "A: $sa1(0) B: $sb1(1)", "$ob $mb1"); t ("s7", "A: $sab(0) B: $sb1(1)", "$oa $mab"); t ("s8", "A: $sab(0) B: $sb2(1)", "$ob $mb2"); t ("s9", "A: $sab(0) B: $sb1(1)", "$ob $mb1"); # TODO: should be B: $sab(0) someday (auto re-cloning)" #t ("s10", "A: $sab(0) B: $sab(0)", "$ob $mab"); t ("p11", "A: $sab(0) B: $sab(0)", "$oa --crtc 0 $ob $mab --crtc 0"); t ("s11", "A: $sa1(1) B: $sab(0)", "$oa $ma1"); t ("s12", "A: $sa1(1) B: $sb1(0)", "$ob $mb1"); t ("s13", "A: $sa1(1) B: $sab(0)", "$ob $mab"); t ("s14", "A: $sa2(1) B: $sab(0)", "$oa $ma2"); t ("s15", "A: $sa1(1) B: $sab(0)", "$oa $ma1"); t ("p16", "A: $sab(0) B: $sab(0)", "$oa $mab --crtc 0 $ob --crtc 0"); t ("s16", "A: $sab(1) B: $sab(0)", "$oa --pos 10x0"); t ("p17", "A: $sab(0) B: $sab(0)", "$oa --crtc 0 $ob --crtc 0"); t ("s17", "A: $sab(0) B: $sab(1)", "$ob --pos 10x0"); t ("p18", "A: $sab(0) B: $sab(0)", "$oa --crtc 0 $ob --crtc 0"); # TODO: s18-s19 are known to fail t ("s18", "A: $sab(1) B: $sab(0)", "$oa --crtc 1"); t ("p19", "A: $sab(1) B: $sab(1)", "$oa --crtc 1 $ob --crtc 1"); t ("s19", "A: $sab(0) B: $sab(1)", "$oa --pos 10x0"); # Part 2: Complex dual output switching tests # TODO: d1 is known to fail t ("pd1", "A: $sab(0)", "$oa --crtc 0 $ob --off"); t ("d1", "B: $sab(0)", "$oa --off $ob $mab"); # Done print "All tests succeeded.\n"; exit 0;