Skip to content

Commit

Permalink
Merge pull request #1508 from lruzicka/master
Browse files Browse the repository at this point in the history
Add `mouse_drag` subroutine to support click and drag in tests
  • Loading branch information
mergify[bot] committed Aug 28, 2020
2 parents 166a4d0 + 168d443 commit e6371b1
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 16 deletions.
141 changes: 141 additions & 0 deletions t/03-testapi.t
Original file line number Diff line number Diff line change
Expand Up @@ -683,4 +683,145 @@ subtest 'autoinst_url' => sub {
is(autoinst_url('foo'), 'http://localhost:1/foo', 'we can configure the hostname that autoinst_url returns');
};

subtest '_calculate_clickpoint' => sub {
my %fake_needle = (
area => [{x => 10, y => 10, w => 20, h => 30}],
);
my %fake_needle_area = (x => 100, y => 100, w => 50, h => 40);
my %fake_click_point = (xpos => 20, ypos => 10);

# Everything is provided.
my ($x, $y) = testapi::_calculate_clickpoint(\%fake_needle, \%fake_needle_area, \%fake_click_point);
is($x, 120);
is($y, 110);

# Everything is provided but the click point is 'center'
($x, $y) = testapi::_calculate_clickpoint(\%fake_needle, \%fake_needle_area, "center");
is($x, 125);
is($y, 120);

# Just the area is provided and no click point.
($x, $y) = testapi::_calculate_clickpoint(\%fake_needle, \%fake_needle_area);
is($x, 125);
is($y, 120);

# Just the needle is provided and no area and click point.
($x, $y) = testapi::_calculate_clickpoint(\%fake_needle);
is($x, 20);
is($y, 25);
};

subtest 'mouse_drag' => sub {
my $mock_testapi = Test::MockModule->new('testapi');
my @area = ({x => 100, y => 100, w => 20, h => 20});
$mock_testapi->redefine(assert_screen => {area => \@area});

my ($startx, $starty) = (0, 0);
my ($endx, $endy) = (200, 200);
my $button = "left";
$cmds = [];
# Startpoint from a needle. Endpoint coordinates.
mouse_drag(startpoint => 'foo', endx => $endx, endy => $endy, button => $button, timeout => 30);
is_deeply($cmds, [
{
cmd => 'backend_mouse_set',
x => 110,
y => 110
},
{
bstate => 1,
button => 'left',
cmd => 'backend_mouse_button'
},
{
cmd => 'backend_mouse_set',
x => 200,
y => 200
},
{
bstate => 0,
button => 'left',
cmd => 'backend_mouse_button'
},
], 'mouse drag (startpoint defined by a needle)') or diag explain $cmds;

# Startpoint from coordinates, endpoint from a needle.
$cmds = [];
mouse_drag(endpoint => 'foo', startx => $startx, starty => $starty, button => $button, timeout => 30);
is_deeply($cmds, [
{
cmd => 'backend_mouse_set',
x => 0,
y => 0
},
{
bstate => 1,
button => 'left',
cmd => 'backend_mouse_button'
},
{
cmd => 'backend_mouse_set',
x => 110,
y => 110
},
{
bstate => 0,
button => 'left',
cmd => 'backend_mouse_button'
},
], 'mouse drag (endpoint defined by a needle)') or diag explain $cmds;

# Using coordinates only.
$cmds = [];
mouse_drag(endx => $endx, endy => $endy, startx => $startx, starty => $starty, button => $button, timeout => 30);
is_deeply($cmds, [
{
cmd => 'backend_mouse_set',
x => 0,
y => 0
},
{
bstate => 1,
button => 'left',
cmd => 'backend_mouse_button'
},
{
cmd => 'backend_mouse_set',
x => 200,
y => 200
},
{
bstate => 0,
button => 'left',
cmd => 'backend_mouse_button'
},
], 'mouse drag (start and endpoints defined by coordinates)') or diag explain $cmds;

# Both needle and coordinates provided for startpoint (coordinates should win).
$cmds = [];
mouse_drag(startpoint => "foo", endx => $endx, endy => $endy, startx => $startx, starty => $starty, button => $button, timeout => 30);
is_deeply($cmds, [
{
cmd => 'backend_mouse_set',
x => 0,
y => 0
},
{
bstate => 1,
button => 'left',
cmd => 'backend_mouse_button'
},
{
cmd => 'backend_mouse_set',
x => 200,
y => 200
},
{
bstate => 0,
button => 'left',
cmd => 'backend_mouse_button'
},
], 'mouse drag (redundant definiton by a needle)') or diag explain $cmds;
};

done_testing;
109 changes: 93 additions & 16 deletions testapi.pm
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ our @EXPORT = qw($realname $username $password $serialdev %cmd %vars
assert_screen check_screen assert_and_dclick save_screenshot
assert_and_click mouse_hide mouse_set mouse_click
mouse_dclick mouse_tclick match_has_tag click_lastmatch
mouse_dclick mouse_tclick match_has_tag click_lastmatch mouse_drag
assert_script_run script_run assert_script_sudo script_sudo
script_output validate_script_output
Expand Down Expand Up @@ -108,6 +108,36 @@ os-autoinst is used in the openQA project.

=head1 internal
=head2 _calculate_clickpoint
This subroutine is used to by several subroutines dealing with mouse clicks to calculate
a clickpoint, when only the needle area is available. It takes the area coordinates and
returns the center of that area. It is meant to be a helper subroutine not available
to be used in tests.
=cut

sub _calculate_clickpoint {
my ($needle_to_use, $needle_area, $click_point) = @_;
# If there is no needle area defined, take it from the needle itself.
if (!$needle_area) {
$needle_area = $needle_to_use->{area}->[-1];
}
# If there is no clickpoint defined, or if it has been specifically defined as "center"
# then calculate the click point as a central point of the specified area.
if (!$click_point || $click_point eq 'center') {
$click_point = {
xpos => $needle_area->{w} / 2,
ypos => $needle_area->{h} / 2,
};
}
# Use the click point coordinates (which are relative numbers inside of the area)
# to calculate the absolute click point position.
my $x = int($needle_area->{x} + $click_point->{xpos});
my $y = int($needle_area->{y} + $click_point->{ypos});
return $x, $y;
}

=for stopwords xen hvc0 xvc0 ipmi ttyS
=head2 init
Expand Down Expand Up @@ -522,25 +552,12 @@ sub click_lastmatch {
my $relative_click_point;
for my $area (reverse @{$last_matched_needle->{area}}) {
next unless ($relative_click_point = $area->{click_point});

$relevant_area = $area;
last;
}

# use center of the last area if no area contains click coordinates
if (!$relevant_area) {
$relevant_area = $last_matched_needle->{area}->[-1];
}
if (!$relative_click_point || $relative_click_point eq 'center') {
$relative_click_point = {
xpos => $relevant_area->{w} / 2,
ypos => $relevant_area->{h} / 2,
};
}

# calculate absolute click position and click
my $x = int($relevant_area->{x} + $relative_click_point->{xpos});
my $y = int($relevant_area->{y} + $relative_click_point->{ypos});
# Calculate the absolute click point.
my ($x, $y) = _calculate_clickpoint($last_matched_needle, $relevant_area, $relative_click_point);
bmwqemu::diag("clicking at $x/$y");
mouse_set($x, $y);
if ($args{dclick}) {
Expand Down Expand Up @@ -1549,6 +1566,66 @@ sub mouse_hide(;$) {
query_isotovideo('backend_mouse_hide', {offset => $border_offset});
}

=head2 mouse_drag
mouse_drag([$startpoint, $endpoint, $startx, $starty, $endx, $endy, $button, $timeout]);
Click mouse C<$button>, C<'left'> or C<'right'>, at a given location, hold the button and drag
the mouse to another location where the button is released. You can set the C<$startpoint>
and C<$endpoint> by passing the name of the needle tag, i.e. the mouse drag happens between
the two needle areas. Alternatively, you can set all the coordinates explicitly with C<$startx>,
C<$starty>, C<$endx>, and C<$endy>. You can also set one point using a needle and another one
using coordinates. If both the coordinates and the needle are provided, the coordinates
will be used to set up the locations and the needle location will be overridden.
=cut

sub mouse_drag {
my %args = @_;
my ($startx, $starty, $endx, $endy);
# If full coordinates are provided, work with them as a priority,
if (defined $args{startx} and defined $args{starty}) {
$startx = $args{startx};
$starty = $args{starty};
}
# If the coordinates were not complete, use the needle as a fallback solution.
elsif (defined $args{startpoint}) {
my $startmatch = $args{startpoint};
# Check that the needle exists.
my $start_matched_needle = assert_screen($startmatch, $args{timeout});
# Calculate the click point from the area defined by the needle (take the center of it)
($startx, $starty) = _calculate_clickpoint($start_matched_needle);
}
# If neither coordinates nor a needle is provided, report an error and quit.
else {
die "The starting point of the drag was not correctly provided. Either provide the 'startx' and 'starty' coordinates, or a needle marking the starting point.";
}

# Repeat the same for endpoint coordinates or needles.
if (defined $args{endx} and defined $args{endy}) {
$endx = $args{endx};
$endy = $args{endy};
}
elsif (defined $args{endpoint}) {
my $endmatch = $args{endpoint};
my $end_matched_needle = assert_screen($endmatch, $args{timeout});
($endx, $endy) = _calculate_clickpoint($end_matched_needle);
}
else {
die "The ending point of the drag was not correctly provided. Either provide the 'endx' and 'endy' coordinates, or a needle marking the end point.";
}
# Get the button variable. If no button has been provided, assume the "left" button.
my $button = $args{button} // "left";

# Now, perform the actual mouse drag. Navigate to the startpoint location,
# press and hold the mouse button, then navigate to the endpoint location
# and release the mouse button.
mouse_set($startx, $starty);
query_isotovideo('backend_mouse_button', {button => $button, bstate => 1});
mouse_set($endx, $endy);
query_isotovideo('backend_mouse_button', {button => $button, bstate => 0});
bmwqemu::log_call(message => "Mouse dragged from $startx,$starty to $endx, $endy", button => $button);
}

=head1 multi console support
All C<testapi> commands that interact with the system under test do that
Expand Down

0 comments on commit e6371b1

Please sign in to comment.