Skip to content

Commit

Permalink
Add mouse_drag subroutine to support click and drag in tests
Browse files Browse the repository at this point in the history
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
  • Loading branch information
lruzicka committed Aug 17, 2020
1 parent 45b1092 commit 438c124
Showing 1 changed file with 96 additions and 12 deletions.
108 changes: 96 additions & 12 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,31 @@ 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 = shift;
# Take the needle defined area
my $needle_area = $needle_to_use->{area}->[-1];
# Calculate the relative center from the area width and height
my $needle_relative_clickpoint = {
xpos => $needle_area->{w} / 2,
ypos => $needle_area->{h} / 2,
};
# Add the relative center to the area starting point to get the absolute clickpoint.
my $x = int($needle_area->{x} + $needle_relative_clickpoint->{xpos});
my $y = int($needle_area->{y} + $needle_relative_clickpoint->{ypos});

return $x, $y;
}

=for stopwords xen hvc0 xvc0 ipmi ttyS
=head2 init
Expand Down Expand Up @@ -527,20 +552,16 @@ sub click_lastmatch {
last;
}

my ($x, $y);
# use center of the last area if no area contains click coordinates
if (!$relevant_area) {
$relevant_area = $last_matched_needle->{area}->[-1];
if (!$relevant_area and (!$relative_click_point || $relative_click_point eq "center")) {
($x, $y) = _calculate_clickpoint($last_matched_needle);
}
if (!$relative_click_point || $relative_click_point eq 'center') {
$relative_click_point = {
xpos => $relevant_area->{w} / 2,
ypos => $relevant_area->{h} / 2,
};
else {
# calculate absolute click position and click
$x = int($relevant_area->{x} + $relative_click_point->{xpos});
$y = int($relevant_area->{y} + $relative_click_point->{ypos});
}

# 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});
bmwqemu::diag("clicking at $x/$y");
mouse_set($x, $y);
if ($args{dclick}) {
Expand Down Expand Up @@ -1549,6 +1570,69 @@ 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 =
my $starty =
my $endx =
my $endy = 0;
# 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
Expand Down

0 comments on commit 438c124

Please sign in to comment.