You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
375 lines
14 KiB
375 lines
14 KiB
5 years ago
|
From 6442f889a3e45e782ad4c2e4c737442e09f895f5 Mon Sep 17 00:00:00 2001
|
||
|
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20R=C5=AF=C5=BEi=C4=8Dka?=
|
||
|
<lruzicka@redhat.com>
|
||
|
Date: Mon, 17 Aug 2020 13:35:10 +0200
|
||
|
Subject: [PATCH 1/3] Add `mouse_drag` subroutine to support click and drag in
|
||
|
tests
|
||
|
|
||
|
1. This PR adds the `mouse_drag` subroutine that enables the mouse and drag
|
||
|
support for OpenQA tests. To use the subroutine, it is possible to either
|
||
|
provide a "starting" and "ending" needle from which the starting and finishing
|
||
|
points will be calculated (using the centres of the needle tag), or
|
||
|
coordinates might be provided directly. If both needles and coordinates are
|
||
|
provided at the same time, then the coordinates precede over the needles.
|
||
|
Combinations are possible, too, which means that one of the points can
|
||
|
be passed as a needle and the other as coordinates.
|
||
|
|
||
|
2. In order not to repete code, the part responsible for calculating the
|
||
|
center of needle was removed from the `click_lastmatch` subroutine and moved
|
||
|
into a standalone non-exported subroutine called `calculate_clickpoint` to be
|
||
|
also used by the `mouse_drag` subroutine, as well as by the original
|
||
|
`click_lastmatch`.
|
||
|
|
||
|
3. This PR solves action #39335
|
||
|
---
|
||
|
testapi.pm | 109 +++++++++++++++++++++++++++++++++++++++++++++--------
|
||
|
1 file changed, 93 insertions(+), 16 deletions(-)
|
||
|
|
||
|
diff --git a/testapi.pm b/testapi.pm
|
||
|
index ed94bd5875..22a062e873 100755
|
||
|
--- a/testapi.pm
|
||
|
+++ b/testapi.pm
|
||
|
@@ -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
|
||
|
@@ -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
|
||
|
@@ -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}) {
|
||
|
@@ -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";
|
||
|
+ bmwqemu::log_call("mouse dragged", button => $button);
|
||
|
+
|
||
|
+ # 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});
|
||
|
+}
|
||
|
+
|
||
|
=head1 multi console support
|
||
|
|
||
|
All C<testapi> commands that interact with the system under test do that
|
||
|
|
||
|
From 5e6d7d1f868430de25cbd1ae6f1168d74d29bed6 Mon Sep 17 00:00:00 2001
|
||
|
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20R=C5=AF=C5=BEi=C4=8Dka?=
|
||
|
<lruzicka@redhat.com>
|
||
|
Date: Tue, 25 Aug 2020 10:04:01 +0200
|
||
|
Subject: [PATCH 2/3] Write tests for _calculate_clickpoint
|
||
|
|
||
|
---
|
||
|
t/03-testapi.t | 28 ++++++++++++++++++++++++++++
|
||
|
1 file changed, 28 insertions(+)
|
||
|
|
||
|
diff --git a/t/03-testapi.t b/t/03-testapi.t
|
||
|
index d53aa12590..edca0eb418 100755
|
||
|
--- a/t/03-testapi.t
|
||
|
+++ b/t/03-testapi.t
|
||
|
@@ -683,4 +683,32 @@ 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);
|
||
|
+};
|
||
|
+
|
||
|
done_testing;
|
||
|
|
||
|
From 168d4437c7de938c50609331ffb26a6241a343a1 Mon Sep 17 00:00:00 2001
|
||
|
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20R=C5=AF=C5=BEi=C4=8Dka?=
|
||
|
<lruzicka@redhat.com>
|
||
|
Date: Wed, 26 Aug 2020 10:32:02 +0200
|
||
|
Subject: [PATCH 3/3] Write tests for mouse_drag
|
||
|
|
||
|
---
|
||
|
t/03-testapi.t | 113 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
testapi.pm | 2 +-
|
||
|
2 files changed, 114 insertions(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/t/03-testapi.t b/t/03-testapi.t
|
||
|
index edca0eb418..e25b831765 100755
|
||
|
--- a/t/03-testapi.t
|
||
|
+++ b/t/03-testapi.t
|
||
|
@@ -711,4 +711,117 @@ subtest '_calculate_clickpoint' => sub {
|
||
|
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;
|
||
|
diff --git a/testapi.pm b/testapi.pm
|
||
|
index 22a062e873..fe16c29b07 100755
|
||
|
--- a/testapi.pm
|
||
|
+++ b/testapi.pm
|
||
|
@@ -1615,7 +1615,6 @@ sub mouse_drag {
|
||
|
}
|
||
|
# Get the button variable. If no button has been provided, assume the "left" button.
|
||
|
my $button = $args{button} // "left";
|
||
|
- bmwqemu::log_call("mouse dragged", button => $button);
|
||
|
|
||
|
# Now, perform the actual mouse drag. Navigate to the startpoint location,
|
||
|
# press and hold the mouse button, then navigate to the endpoint location
|
||
|
@@ -1624,6 +1623,7 @@ sub mouse_drag {
|
||
|
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
|