Android-ElectricSheep-Automator

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

    
        # bottom navigation:
        # the "triangle" back button
        $mother->navigation_menu_back_button() or die;
        # the "circle" home button
        $mother->navigation_menu_home_button() or die;
        # the "square" overview button
        $mother->navigation_menu_overview_button() or die;
    
        # open/close apps
        $mother->open_app({'package'=>qr/calendar$/i}) or die;
        $mother->close_app({'package'=>qr/calendar$/i}) or die;
    
        # push pull files
        $mother->adb->pull($deviceFile, $localFile);
        $mother->adb->push($localFile, $deviceFileOrDir);
    
        # guess what!
        my $xmlstr = $mother->dump_current_screen_ui();

CONSTRUCTOR

README  view on Meta::CPAN

    and running with, for example, emulator -avd Pixel_2_API_30_x86_ . See
    section "Android Emulators" for how to install, list and run them
    buggers.

    Testing will not send any messages via the device's apps. E.g. the
    plugin Android::ElectricSheep::Automator::Plugins::Apps::Viber will not
    send a message via Viber but it will mock it.

    The live tests will sometimes fail because, so far, something
    unexpected happened in the device. For example, in testing sending
    input text to a text-edit widget, the calendar will be opened and a new
    entry will be added and its text-edit widget will be targeted. Well,
    sometimes the calendar app will give you some notification on startup
    and this messes up with the focus. Other times, the OS will detect that
    some app is taking too long to launch and pops up a notification about
    "something is not responding, shall I close it". This steals the focus
    and sometimes it causes the tests to fail.

PREREQUISITES

 Android Studio

    This is not a prerequisite but it is highly recommended to install

README.md  view on Meta::CPAN


    # bottom navigation:
    # the "triangle" back button
    $mother->navigation_menu_back_button() or die;
    # the "circle" home button
    $mother->navigation_menu_home_button() or die;
    # the "square" overview button
    $mother->navigation_menu_overview_button() or die;

    # open/close apps
    $mother->open_app({'package'=>qr/calendar$/i}) or die;
    $mother->close_app({'package'=>qr/calendar$/i}) or die;

    # push pull files
    $mother->adb->pull($deviceFile, $localFile);
    $mother->adb->push($localFile, $deviceFileOrDir);

    # guess what!
    my $xmlstr = $mother->dump_current_screen_ui();

# CONSTRUCTOR

README.md  view on Meta::CPAN

["Android Emulators"](#android-emulators) for how to install, list and run them
buggers.

Testing will not send any messages via the device's apps.
E.g. the plugin [Android::ElectricSheep::Automator::Plugins::Apps::Viber](https://metacpan.org/pod/Android%3A%3AElectricSheep%3A%3AAutomator%3A%3APlugins%3A%3AApps%3A%3AViber)
will not send a message via Viber but it will mock it.

The live tests will sometimes fail because, so far,
something unexpected happened in the device. For example,
in testing sending input text to a text-edit widget,
the calendar will be opened and a new entry will be added
and its text-edit widget will be targeted. Well, sometimes
the calendar app will give you some notification
on startup and this messes up with the focus.
Other times, the OS will detect that some app is taking too
long to launch and pops up a notification about
"_something is not responding, shall I close it_".
This steals the focus and sometimes it causes
the tests to fail.

# PREREQUISITES

## Android Studio

lib/Android/ElectricSheep/Automator.pm  view on Meta::CPAN

		# if this is undef, then it means caller did not call connect_device()
		# when caller calls disconnect_device(), this becomes undef again
		# this is a cheap way to not proceed to device-needed subs, e.g. swipe()
		# of course we could make an adb query with e.g. adb get-state
		'device-properties' => undef,

		# object of type Android::ElectricSheep::Automator::ADB::Device
		# which is created when we call connect_device()
		'device-object' => undef,

		# a hash of installed apps by package name (e.g. android.google.calendar)
		# the value will be an AppProperties object if it was enquired or undef
		# if it wasn't. As the addition of apps is done in a lazy way, when
		# needed, unless specified otherwise. In any event open_app() will add an
		# AppProperties object if missing to the specified package.
		'apps' => {},

		# legacy, no worries.
		'apps-roundabout-way' => undef,
	};
	bless $self => $class;

lib/Android/ElectricSheep/Automator.pm  view on Meta::CPAN


    # bottom navigation:
    # the "triangle" back button
    $mother->navigation_menu_back_button() or die;
    # the "circle" home button
    $mother->navigation_menu_home_button() or die;
    # the "square" overview button
    $mother->navigation_menu_overview_button() or die;

    # open/close apps
    $mother->open_app({'package'=>qr/calendar$/i}) or die;
    $mother->close_app({'package'=>qr/calendar$/i}) or die;

    # push pull files
    $mother->adb->pull($deviceFile, $localFile);
    $mother->adb->push($localFile, $deviceFileOrDir);

    # guess what!
    my $xmlstr = $mother->dump_current_screen_ui();

=head1 CONSTRUCTOR

lib/Android/ElectricSheep/Automator.pm  view on Meta::CPAN

L<Android Emulators> for how to install, list and run them
buggers.

Testing will not send any messages via the device's apps.
E.g. the plugin L<Android::ElectricSheep::Automator::Plugins::Apps::Viber>
will not send a message via Viber but it will mock it.

The live tests will sometimes fail because, so far,
something unexpected happened in the device. For example,
in testing sending input text to a text-edit widget,
the calendar will be opened and a new entry will be added
and its text-edit widget will be targeted. Well, sometimes
the calendar app will give you some notification
on startup and this messes up with the focus.
Other times, the OS will detect that some app is taking too
long to launch and pops up a notification about
"I<something is not responding, shall I close it>".
This steals the focus and sometimes it causes
the tests to fail.

=head1 PREREQUISITES

=head2 Android Studio

lib/Android/ElectricSheep/Automator/AppProperties.pm  view on Meta::CPAN


	# NOTE
	#   dumpsys package packages
	# and 
	#   dumpsys package com.example.xyz 
	# will yield different things for the package com.example.xyz 
	# we need to enquire twice, 1 for all the package names
	# 2 for the full content of the package info, including activities
	# we will dumpsys specifically for that package
	my $package;
	if( ! exists($params->{'package'}) || ! defined($package=$params->{'package'}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter 'package' (the package name e.g. com.example.calendar) was not specified. If you want ...

	if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : package '${package}' : called ...") }

	# here we could also save to a file on device and then
	# fetch it locally. We will do that if there are problems
	# getting the dump from STDOUT
	my @cmd = ('dumpsys', 'package', $package);
	my $res = $self->adb->shell(@cmd);
	if( ! defined $res ){ $log->error(join(" ", @cmd)."\n${whoami} (via $parent), line ".__LINE__." : error, above shell command has failed, got undefined result, most likely shell command did not run at all, this should not be happening."); return unde...
	if( $res->[0] != 0 ){ $log->error(join(" ", @cmd)."\n${whoami} (via $parent), line ".__LINE__." : error, above shell command has failed, with:\nsSTDOUT:\n".$res->[1]."\n\nSTDERR:\n".$res->[2]."\nEND."); return undef }

xt/live/260-find_all_apps-search_app.t  view on Meta::CPAN


my $mother = Android::ElectricSheep::Automator->new({
	'configfile' => $configfile,
	#'verbosity' => $VERBOSITY,
	# we have a device connected and ready to control
	'device-is-connected' => 1,
});
ok(defined($mother), 'Android::ElectricSheep::Automator->new()'." : called and got defined result.") or BAIL_OUT;

##############################################################
# find one app by regex name 'calendar' this yields 2 matches
# we expect to have lots of apps but only 2 to be instantiated
#   com.google.android.calendar
#   com.android.providers.calendar
my $aregex = qr/calendar/i;
my $params = {
	'packages' => $aregex, # this will instantiate all matched apps AppProperties
	# default is lazy=1
};
my $apps = $mother->find_installed_apps($params);
ok(defined($apps), 'find_installed_apps()'." : called and got good result.") or BAIL_OUT;
is(ref($apps), 'HASH', 'find_installed_apps()'." : called and result is a HASHref.") or BAIL_OUT("no it is '".ref($apps)."'.");
ok(scalar(keys %$apps)>1, 'find_installed_apps()'." : called and result contains at least one item.") or BAIL_OUT("no it contains ".scalar(keys %$apps)." items.");
is_deeply($apps, $mother->apps, 'find_installed_apps()'." : set mother's apps to the returned value.") or BAIL_OUT;
# now $mother's apps must contain many items but calendar will have AppProperties.
my $instantiated_apps = 0;
my @instantiated_apps = ();
for my $appname (sort keys %$apps){
	if( defined $apps->{$appname} ){
		$instantiated_apps++;
		push @instantiated_apps, $appname;
	}
	if( $appname =~ $aregex ){
		ok(defined($apps->{$appname}), 'find_installed_apps()'." : app '$appname' has AppProperties.") or BAIL_OUT;
		for my $k (qw/

xt/live/260-find_all_apps-search_app.t  view on Meta::CPAN

ok(defined($searched_apps), 'search_app()'." : called and got good result") or BAIL_OUT;
is(ref($searched_apps), 'HASH', 'search_app()'." : called and result is a HASHref.") or BAIL_OUT(perl2dump($params)."using above params, no it is '".ref($apps)."'.");
ok(scalar(keys %$searched_apps)==1 || scalar(keys %$searched_apps)==2, 'search_app()'." : called and result contains one or two items.") or BAIL_OUT(perl2dump($params)."using above params, no it contains ".scalar(keys %$searched_apps)." items: ".join...

# we must still have the same number of total apps in mother
is(scalar keys %{ $mother->apps }, $num_apps_total, "after these operations the number of total apps remains unchanged ($num_apps_total).") or BAIL_OUT(perl2dump($params)."using above params, no it is now ".(scalar keys %{ $mother->apps })." instead ...

##############################################################
# find same apps as before with an ARRAY
# we expect to have lots of apps but only 2 to be instantiated
#   com.google.android.calendar
#   com.android.providers.calendar
$aregex = qr/^(\Qcom.google.android.calendar\E)|(\Qcom.android.providers.calendar\E)$/i;
$params = {
	'packages' => [
		'com.google.android.calendar',
		'com.android.providers.calendar'
	],
	# default is lazy=1
};
$apps = $mother->find_installed_apps($params);
ok(defined($apps), 'find_installed_apps()'." : called and got good result.") or BAIL_OUT;
is(ref($apps), 'HASH', 'find_installed_apps()'." : called and result is a HASHref.") or BAIL_OUT(perl2dump($params)."using above params, no it is '".ref($apps)."'.");
ok(scalar(keys %$apps)>1, 'find_installed_apps()'." : called and result contains at least one item.") or BAIL_OUT(perl2dump($params)."using above params, no it contains ".scalar(keys %$apps)." items.");
is_deeply($apps, $mother->apps, 'find_installed_apps()'." : set mother's apps to the returned value.") or BAIL_OUT;
# now $mother's apps must contain many items but calendar will have AppProperties.
my $instantiated_apps = 0;
my @instantiated_apps = ();
for my $appname (sort keys %$apps){
	if( defined $apps->{$appname} ){
		$instantiated_apps++;
		push @instantiated_apps, $appname;
	}
	if( $appname =~ $aregex ){
		ok(defined($apps->{$appname}), 'find_installed_apps()'." : app '$appname' has AppProperties.") or BAIL_OUT;
		for my $k (qw/

xt/live/260-find_all_apps-search_app.t  view on Meta::CPAN

ok(defined($searched_apps), 'search_app()'." : called and got good result") or BAIL_OUT;
is(ref($searched_apps), 'HASH', 'search_app()'." : called and result is a HASHref.") or BAIL_OUT(perl2dump($params)."using above params, no it is '".ref($apps)."'.");
ok(scalar(keys %$searched_apps)==1 || scalar(keys %$searched_apps)==2, 'search_app()'." : called and result contains one or two items.") or BAIL_OUT(perl2dump($params)."using above params, no it contains ".scalar(keys %$searched_apps)." items: ".join...

# we must still have the same number of total apps in mother
is(scalar keys %{ $mother->apps }, $num_apps_total, "after these operations the number of total apps remains unchanged ($num_apps_total).") or BAIL_OUT(perl2dump($params)."using above params, no it is now ".(scalar keys %{ $mother->apps })." instead ...

##############################################################
# find same apps as before with a HASH
# we expect to have lots of apps but only 2 to be instantiated
#   com.google.android.calendar
#   com.android.providers.calendar
$aregex = qr/^(\Qcom.google.android.calendar\E)|(\Qcom.android.providers.calendar\E)$/i;
$params = {
	'packages' => {
		'com.google.android.calendar' => 1,
		'com.android.providers.calendar' => 1,
	},
	# default is lazy=1
};
$apps = $mother->find_installed_apps($params);
ok(defined($apps), 'find_installed_apps()'." : called and got good result.") or BAIL_OUT;
is(ref($apps), 'HASH', 'find_installed_apps()'." : called and result is a HASHref.") or BAIL_OUT(perl2dump($params)."using above params, no it is '".ref($apps)."'.");
ok(scalar(keys %$apps)>1, 'find_installed_apps()'." : called and result contains at least one item.") or BAIL_OUT(perl2dump($params)."using above params, no it contains ".scalar(keys %$apps)." items.");
is_deeply($apps, $mother->apps, 'find_installed_apps()'." : set mother's apps to the returned value.") or BAIL_OUT;
# now $mother's apps must contain many items but calendar will have AppProperties.
my $instantiated_apps = 0;
my @instantiated_apps = ();
for my $appname (sort keys %$apps){
	if( defined $apps->{$appname} ){
		$instantiated_apps++;
		push @instantiated_apps, $appname;
	}
	if( $appname =~ $aregex ){
		ok(defined($apps->{$appname}), 'find_installed_apps()'." : app '$appname' has AppProperties.") or BAIL_OUT;
		for my $k (qw/

xt/live/260-find_all_apps-search_app.t  view on Meta::CPAN

$params = {
	'packages' => $aregex, # this will instantiate all matched apps AppProperties
	# this will instantiate all apps
	'lazy' => 0,
};
$apps = $mother->find_installed_apps($params);
ok(defined($apps), 'find_installed_apps()'." : called and got good result.") or BAIL_OUT;
is(ref($apps), 'HASH', 'find_installed_apps()'." : called and result is a HASHref.") or BAIL_OUT(perl2dump($params)."using above params, no it is '".ref($apps)."'.");
ok(scalar(keys %$apps)>1, 'find_installed_apps()'." : called and result contains at least one item.") or BAIL_OUT(perl2dump($params)."using above params, no it contains ".scalar(keys %$apps)." items.");
is_deeply($apps, $mother->apps, 'find_installed_apps()'." : set mother's apps to the returned value.") or BAIL_OUT;
# now $mother's apps must contain many items but calendar will have AppProperties.
$instantiated_apps = 0;
@instantiated_apps = ();
for my $appname (sort keys %$apps){
	if( defined $apps->{$appname} ){
		$instantiated_apps++;
		push @instantiated_apps, $appname;
	}
	if( $appname =~ $aregex ){
		ok(defined($apps->{$appname}), 'find_installed_apps()'." : app '$appname' has AppProperties.") or BAIL_OUT;
		if(0){

xt/live/280-open_app-close_app.t  view on Meta::CPAN


my $mother = Android::ElectricSheep::Automator->new({
	'configfile' => $configfile,
	#'verbosity' => $VERBOSITY,
	# we have a device connected and ready to control
	'device-is-connected' => 1,
});
ok(defined($mother), 'Android::ElectricSheep::Automator->new()'." : called and got defined result.") or BAIL_OUT;

# open an app
my $aregex = qr/^com\.google\.android\.calendar$/i;
my $res = $mother->open_app({
	'package' => $aregex
});
ok(defined($res), 'Android::ElectricSheep::Automator->open_app()'." : called and got good result.") or BAIL_OUT;
is(ref($res), 'HASH', 'Android::ElectricSheep::Automator->open_app()'." : called and got good result which is a HASHref.") or BAIL_OUT("no it is '".ref($res)."'");
for my $k (keys %$res){
	is(ref($res->{$k}), 'Android::ElectricSheep::Automator::AppProperties', 'Android::ElectricSheep::Automator->open_app()'." : called and got good result which is of type 'Android::ElectricSheep::Automator::AppProperties'.") or BAIL_OUT("no it is '".re...
}

# now we will have lots of apps but only settings will have AppProperties

xt/live/420-adb-pidof.t  view on Meta::CPAN

my $mother = Android::ElectricSheep::Automator->new({
	'configfile' => $configfile,
	#'verbosity' => $VERBOSITY,
	# we have a device connected and ready to control
	'device-is-connected' => 1,
});
ok(defined($mother), 'Android::ElectricSheep::Automator->new()'." : called and got defined result.") or BAIL_OUT;

# this app should exist
# we are sure this app exists on a virgin phone i guess?
my $name = 'com.google.android.calendar';
my $res = $mother->pidof({'name' => $name});
ok(defined($res), 'Android::ElectricSheep::Automator->pidof()'." : called and got good result.") or BAIL_OUT;
is(ref($res), '', 'Android::ElectricSheep::Automator->pidof()'." : called and got good result which is a SCALAR.") or BAIL_OUT("no it is '".ref($res)."'");
ok($res >= 0, 'Android::ElectricSheep::Automator->pidof()'." : called and got good result which is a pid > 0 (meaning success).") or BAIL_OUT("no it is '$res'.");
diag "got pid of app '$name' to be '$res'.";

# this should not exist
# we are sure this app exists on a virgin phone i guess?
my $name = 'crazy.xxx.yyy.zzz';
my $res = $mother->pidof({'name' => $name});

xt/live/440-adb-pgrep.t  view on Meta::CPAN

	ok($ares->{$k} !~ /^\s*$/, 'Android::ElectricSheep::Automator->pgrep()'." : result is an ARRAY of hashes and this one contains key '$k'.") or BAIL_OUT(perl2dump($ares)."no it contains above.");
  }
  for my $k ('command'){ # this must exist but must be empty string
	ok(exists($ares->{$k}), 'Android::ElectricSheep::Automator->pgrep()'." : result is an ARRAY of hashes and this one contains key '$k'.") or BAIL_OUT(perl2dump($ares)."no it contains above.");
	ok(defined($ares->{$k}), 'Android::ElectricSheep::Automator->pgrep()'." : result is an ARRAY of hashes and this one contains key '$k'.") or BAIL_OUT(perl2dump($ares)."no it contains above.");
	#ok($ares->{$k} !~ /^\s*$/, 'Android::ElectricSheep::Automator->pgrep()'." : result is an ARRAY of hashes and this one contains key '$k'.") or BAIL_OUT(perl2dump($ares)."no it contains above.");
  }
}

# this is an exact match, it should return 1 pid
$name = '^com.google.android.calendar$';
$res = $mother->pgrep({'name' => $name});
ok(defined($res), 'Android::ElectricSheep::Automator->pgrep()'." : called and got good result.") or BAIL_OUT;
is(ref($res), 'ARRAY', 'Android::ElectricSheep::Automator->pgrep()'." : called and got good result which is an ARRAY.") or BAIL_OUT("no it is '".ref($res)."'");
is(scalar(@$res), 1, 'Android::ElectricSheep::Automator->pgrep()'." : called and got good result which is an ARRAY with exactly 1 item because an exact name was specified.") or BAIL_OUT(perl2dump($res)."no it is above.");
for my $ares (@$res){
  for my $k ('pid', 'command'){
	ok(exists($ares->{$k}), 'Android::ElectricSheep::Automator->pgrep()'." : result is an ARRAY of hashes and this one contains key '$k'.") or BAIL_OUT(perl2dump($ares)."no it contains above.");
	ok(defined($ares->{$k}), 'Android::ElectricSheep::Automator->pgrep()'." : result is an ARRAY of hashes and this one contains key '$k'.") or BAIL_OUT(perl2dump($ares)."no it contains above.");
	ok($ares->{$k} !~ /^\s*$/, 'Android::ElectricSheep::Automator->pgrep()'." : result is an ARRAY of hashes and this one contains key '$k'.") or BAIL_OUT(perl2dump($ares)."no it contains above.");
  }

xt/live/460-is_app_running.t  view on Meta::CPAN

my $mother = Android::ElectricSheep::Automator->new({
	'configfile' => $configfile,
	#'verbosity' => $VERBOSITY,
	# we have a device connected and ready to control
	'device-is-connected' => 1,
});
ok(defined($mother), 'Android::ElectricSheep::Automator->new()'." : called and got defined result.") or BAIL_OUT;

# this app should exist
# we are sure this app exists on a virgin phone i guess?
my $appname = 'com.google.android.calendar';
my $res = $mother->is_app_running({'appname' => $appname});
ok(defined($res), 'Android::ElectricSheep::Automator->is_app_running()'." : called and got good result.") or BAIL_OUT;
is(ref($res), '', 'Android::ElectricSheep::Automator->is_app_running()'." : called and got good result which is a SCALAR.") or BAIL_OUT("no it is '".ref($res)."'");
is($res, 1, 'Android::ElectricSheep::Automator->is_app_running()'." : called and got good result which is 1 meaning the app is running as expected.") or BAIL_OUT("no it is '$res'.");

# this should not exist
# we are sure this app exists on a virgin phone i guess?
my $appname = 'crazy.xxx.yyy.zzz';
my $res = $mother->is_app_running({'appname' => $appname});
ok(defined($res), 'Android::ElectricSheep::Automator->is_app_running()'." : called and got good result.") or BAIL_OUT;



( run in 0.388 second using v1.01-cache-2.11-cpan-5dc5da66d9d )