I want to know, does any way exist to communicate with system during instrumentation test execution.
For example:
I have a phone with IR port on onboard & I can work with it through private SDK, also I can tune it with my application. In my Instrumentation test cases I want test app behavior based on external events which I want to configure before test separate test execution.
It's looks like
#Test
public void test() throws Exception {
setupExternalCondition(condition1_ON); // setup external transiver
assertNotNull(IR.read());
assertTrue(assertIR.write());
setupExternalCondition(condition1_OFF);
assertNotNull(IR.read());
assertFalse(IR.write());
}
It's very simple example but there is a lot of "conditions", and sdk updating frequencies to high. I can't do all of this verification manually, and can't ask "transiver&SDK team" make a mock states list for writing just a unit test for coverage. So I want somehow inject external component execution to TestRuner for receiving events(or testName before test case execution) on local machine(or CI machine) to setup external condition.
Simple solution(I think) to run a tcp server on appUnderTest and request external condition change - I am not sure does it possible, and not sure about stable connection(wifi), so may be it's possible to do over adb.
Any suggestions?
P.S: test device has root permissions.
So, find not bad but not ideal solution.
Still wait for better proposition, if not may be this answer will be helpful for someone;
For "building the bridge" between local machine and AndroidJUnitTest I add next class to tests:
class IPCServiceBridge extends BroadcastReceiver {
private static final String FILTER_ID = "IPC_SERVICE";
private static IPCServiceBridge sInstance;
private boolean mIsPermitted;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("ipc.service.action")) {
mIsPermitted = true;
}
}
public static IPCServiceBridge getInstance() {
if (sInstance == null) {
sInstance = new IPCServiceBridge();
IntentFilter filter = new IntentFilter();
filter.addAction("ipc.service.action");
Context context = InstrumentationRegistry.getContext();
context.registerReceiver(sInstance, filter);
}
return sInstance;
}
public void sendIpcCommand(String commandName) {
try {
int i = 30;
mIsPermitted = false;
while (i > 0) {
pub("request:" + commandName);
Thread.sleep(1000);
if (mIsPermitted) {
break;
}
i--;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (!mIsPermitted) {
throw new RuntimeException("IPC service does not respond");
}
}
private static void pub(String msg) {
Log.e(FILTER_ID, msg);
}
}
Than I start adb logcat -s "filter_name", parse and check which condition should be applied for InsttUnit test. When conditions is ready i send back broadcast receiver with required action.
#Test
public void test2() throws Exception {
IPCServiceBridge.getInstance().sendIpcCommand("CONDITION#123");
}
Work good, but I'm not sure that it will be super stable.
Related
I need help. There is an application for Xamarin Android. Inside it, a service is started that works with the AltBeacon library.
In this service, a thread is created, in which beacons are constantly being scanned. Service started as StartForeground(...).
The service should work constantly, so it was decided to run it in a separate process, because after a while the android system stops allocating
memory and service terminates.
If you run the application and the service in one process, everything works fine. Service works, beacons are scanned.
But as soon as I run it in a separate process (using the Process =: myProcess attribute), the scanning not works.
The DidRangeBeaconsInRegion method does not work for the IRangeNotifier implementation object.
It simply does not work, there are no exceptions.
Brief code base:
public class BeaconsWorker : Java.Lang.Object, IBeaconConsumer
{
private string[] guids;
private readonly Context context;
private readonly BeaconManager beaconManager;
private readonly RangeNotifier rangeNotifier;
private readonly List<BeaconEntry> beacons;
public Context ApplicationContext
{
get { return context.ApplicationContext; }
}
public BeaconsWorker(Context context, string[] guids, int scanTime)
{
...
this.context = context;
rangeNotifier = new RangeNotifier();
beaconManager = BeaconManager.GetInstanceForApplication(context);
beaconManager.SetForegroundBetweenScanPeriod(1000);
beaconManager.SetForegroundScanPeriod(1000);
beaconManager.SetBackgroundMode(false);
var beaconParser = new BeaconParser();
beaconParser.SetBeaconLayout("...");
beaconManager.BeaconParsers.Add(beaconParser);
rangeNotifier.DidRangeBeaconsInRegionComplete += OnBeaconsRanging;
beaconManager.SetRangeNotifier(rangeNotifier);
}
public bool BindService(Intent intent, IServiceConnection serviceConnection, [GeneratedEnum] Bind flags)
{
return context.BindService(intent, serviceConnection, flags);
}
public void OnBeaconServiceConnect()
{
foreach (var region in beaconManager.RangedRegions.ToList())
beaconManager.StopRangingBeaconsInRegion(region);
for (int i = 0; i < guids.Length; i++)
{
var uuid = Identifier.Parse(guids[i]);
var region = new Region("R" + i, uuid, null, null);
beaconManager.StartRangingBeaconsInRegion(region);
}
}
public void UnbindService(IServiceConnection serviceConnection)
{
context.UnbindService(serviceConnection);
}
public async Task<BeaconEntry> GetLocationResult()
{
beaconManager.Bind(this);
await Task.Delay(scanTime * 1000);
beaconManager.Unbind(this);
...
return result;
}
private void OnBeaconsRanging(object sender, RangeEventArgs e)
{
lock (beacons)
foreach (var item in e.Beacons)
{
var beacon = new BeaconEntry()
{
BeaconGUID = item.Id1.ToString(),
BeaconMajor = Int32.Parse(item.Id2.ToString()),
BeaconMinor = Int32.Parse(item.Id3.ToString())
};
beacons.Add(beacon);
}
}
private class RangeEventArgs : EventArgs
{
public Region Region { get; set; }
public ICollection<Beacon> Beacons { get; set; }
}
private class RangeNotifier : Java.Lang.Object, IRangeNotifier
{
public event EventHandler<RangeEventArgs> DidRangeBeaconsInRegionComplete;
public void DidRangeBeaconsInRegion(ICollection<Beacon> beacons, Region region)
{
OnDidRangeBeaconsInRegion(beacons, region);
}
private void OnDidRangeBeaconsInRegion(ICollection<Beacon> beacons, Region region)
{
DidRangeBeaconsInRegionComplete?.Invoke(this, new RangeEventArgs { Beacons = beacons, Region = region });
}
}
It is possible to set up the Android Beacon Library to run in a separate process. You can read the basic configuration instructions here:
https://github.com/AltBeacon/android-beacon-library/pull/479
This multi-process setup was successfully tested with library version 2.11. Version 2.12, however, included significant rework to support Android 8, and I have not tested multi-process support with versions 2.12+, so use those versions with caution. Your best bet is to use version 2.11.
The instructions linked above are written for Android apps built using standard Java or Kotlin development toolset with Android Studio or Gradle. Clearly modifications are needed to make this work with Xamarin. Since I am not a Xamarin expert, it's hard for me to help more.
Is there a way to create a SOCKS-5 proxy server on an unrooted android device? Why does it require root? Can I make my own android application that will create a server on my device and use this server as proxy on my compluter, all this without rooting the device?
UPDATE: I came to using this library to try to create a proxy server. I downloaded all files, but added only main .jar file to the project dependencies in AndroidStudio. There I am trying to start the server from a runnable in a service. Here is the service code:
public class ProxyService extends Service
{
private static boolean running = false;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
ServerAuthenticatorNone auth = new ServerAuthenticatorNone();
ProxyServer server = new ProxyServer(auth);
Run(server); //runnable is needed because if I start service from
//this thread it throws a network on main thread exception
return START_STICKY;
}
private void Run(ProxyServer server) {
if (running)
return;
final ProxyServer serverFinal = server;
running = true;
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
Log.i("DEBUG", "tick " + running);
serverFinal.start(1080);
Log.i("DEBUG", "proxyServer started at port 1080");
running = true;
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
running = false;
}
}
}
}).start();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
When I run this code on my Xiaomi Redmi Note 2 unrooted device, I get the following logs:
07-26 16:48:18.097: I/DEBUG(19766): tick true
07-26 16:48:18.099: I/XiaomiFirewall(1211): firewall pkgName:com.socks.server, result:0
And when I check proxy using this website I get nothing.
I think I should have configured the server somehow, but it is done using external .properties file that is outside of the .jar and I do not know how to link them in an AndroidStudio project.
I also suspect that even these actions require superuser(root) to prevent Xiaomi firewall from refusing me in starting the server.
Any advice?
I'm trying to check if my API is available within a unit test, to be sure that it responds with 200.
Now, my problem is that I'm not really sure when to use Local Test and when I have to use Android Instrumentation Tests. I know that I have to use Instrumented Tests for UI testing but how to test the endpoint?
I use Retrofit2 for communication. And tried to test Endpoint in 2 ways with Local Tests.
Example 1 (synchronous, does not work)
public class EndpointTest {
EndpointApi api;
SimpleInjection simpleInjection;
#Before
public void setUp() {
simpleInjection = new SimpleInjection();
api = simpleInjection.getEndpointApi();
}
#Test
public void endpoint_1_isAvailable() {
Call<ApiResponse> rootCall = api.getRoot();
try {
int reponseCode = rootCall.execute().code();
Assert.assertEquals(200, reponseCode);
} catch (IOException e) {
Assert.fail();
}
}
}
Example 2 (asynchronous, does work)
public class EndpointTest {
EndpointApi api;
SimpleInjection simpleInjection;
#Before
public void setUp() {
simpleInjection = new SimpleInjection();
api = simpleInjection.getEndpointApi();
}
#Test
public void endpoint_2_isAvailable() {
Call<ApiResponse> rootCall = api.getRoot();
rootCall.enqueue(new Callback<ApiResponse>() {
#Override
public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) {
Assert.assertEquals(200, response.code());
}
#Override
public void onFailure(Call<ApiResponse> call, Throwable t) {
Assert.fail();
}
});
}
}
Do I have to use Android Instrumentation Test for asynchronous mode?
The decision of whether to run your tests on a local JVM on your development machine or on an Android device/emulator is not based on whether your code is synchronous or not. Usually you would only want to run unit tests locally, as it's a lot faster and allows you to use TDD. Your tests do network requests, so they're not unit tests per say, since they have the dependency on the server - they are integration tests. It's preferable to run integration tests on an Android device to get better feedback.
I need to get access to com.android.internal.telephony.Call.
doing so:
// Initialize the telephony framework
PhoneFactory.makeDefaultPhones (this);
// Get the default phone
Phone phone = PhoneFactory.getDefaultPhone ();
CallManager mCM = CallManager.getInstance ();
mCM.registerPhone (phone);
Call call = mCM.getFirstActiveBgCall();
but does not extend to initialize the framework.
Help me to initialize Call.
I need to read the state of the call like:
IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING.
You need to make use of PhoneStateListener
It will provide you the facility to have your application listen for different state of a phone call. You will need to put <uses-permission android:name="android.permission.READ_PHONE_STATE"/> in your manifest file
You can but there is a critical requirement: the application must be signed at system level, meaning you are the manufacturer.
Here is how you write a Service that will broadcast an intent for every change in the foreground call state.
/*
* This implementation uses the com.android.internal.telephony package: you have
* to extract the framework classes .jar file from the platform (or the
* emulator) to compile this code. Also, add the jar file to the external
* libraries in the Java Build Path/libraries of the android project. </p>
*
* The jar file must match the android version you are building the application
* for. Because this implementation is using the internal packages it cannot be
* guaranteed to operate on later versions of android.
*/
public class CallStateNotificationService extends Service {
private static final String LOG_TAG = CallStateNotificationService.class.getSimpleName();
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if (msg.what == 101) {
CallManager callManager = CallManager.getInstance();
Call.State state = callManager.getActiveFgCallState();
Intent intent = new Intent(PhoneIntents.ACTION_PRECISE_CALL_STATE);
intent.putExtra(PhoneIntents.PRECISE_CALL_STATE, state.name());
Context context = getApplicationContext();
context.sendBroadcast(intent);
}
}
};
#Override
public void onCreate() {
super.onCreate();
try {
CallManager callManager = CallManager.getInstance();
if (callManager != null) {
callManager.registerForPreciseCallStateChanged(mHandler, 101, null);
} else {
Log.w(LOG_TAG, "Can't resolve CallManager reference"); //$NON-NLS-1$
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onDestroy() {
super.onDestroy();
CallManager callManager = CallManager.getInstance();
if (callManager != null) {
callManager.unregisterForPreciseCallStateChanged(mHandler);
} else {
Log.w(LOG_TAG, "Can't resolve CallManager reference"); //$NON-NLS-1$
}
}
And here is the definition of the custom broadcasted intents.
/** Intent action and extra argument names for CallStateNotificationService */
public final class PhoneIntents {
public static final String ACTION_PRECISE_CALL_STATE = "com.myorg.myapp.CALL_STATE";
public static final String PRECISE_CALL_STATE = "precise_call_state";
}
To have this code compile and link, you of course need to either build the program as part of the android distribution itself or import the class-framework by a method explained elsewhere on the Internet.
All of this is currently in an app under production.
Is there a runtime check for an application to find out if it runs as part of an instrumentation test?
Background: Our application performs a database sync when starting. But that should happen only when started regularly. It especially interferes with the instrumentation tests testing the db sync. Not surprisingly.
And with all the other tests it's just a waste of CPU cycles.
A much simpler solution is check for a class that would only be present in a test classpath, works with JUnit 4 (unlike the solution using ActivityUnitTestCase) and doesn't require to send custom intents to your Activities / Services (which might not even be possible in some cases)
private boolean isTesting() {
try {
Class.forName("com.company.SomeTestClass");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
Since API Level 11, the ActivityManager.isRunningInTestHarness() method is available. This might do what you want.
If you are using Robolectric, you can do something like this:
public boolean isUnitTest() {
String device = Build.DEVICE;
String product = Build.PRODUCT;
if (device == null) {
device = "";
}
if (product == null) {
product = "";
}
return device.equals("robolectric") && product.equals("robolectric");
}
If you're using ActivityUnitTestCase, you could set a custom Application object with setApplication, and have a flag in there to switch database sync on or off? There's an example of using a custom Application object on my blog:
http://www.paulbutcher.com/2011/03/mock-objects-on-android-with-borachio-part-3/
You can pass an intent extra to your activity indicating it's under test.
1) In your test, pass "testMode" extra to your activity:
public void setUp() throws Exception {
super.setUp();
Intent activityIntent = new Intent();
activityIntent.putExtra("testMode", true);
setActivityIntent(activityIntent);
}
2) In your activity, check for testMode:
Bundle extras = getIntent().getExtras();
if (extras != null && extras.getBoolean("testMode")) {
// disable your database sync
}
d= (◕‿↼ ) Great answer, but if some library developer (like me) wants to know if the Host (or App using the library) is being tested, then try:
import android.content.pm.ApplicationInfo;
// ...
private static int wasTestRun = 0xDEAD;
/**
* Should only be used to speed up testing (no behavior change).
* #return true in tests, if Gradle has the right dependencies.
*/
public static boolean isTestRun(#NonNull Context context) {
if (wasTestRun != 0xDEAD) {
return wasTestRun != 0;
}
// Ignore release builds (as App may be using JUnit by mistake).
if (isDebuggable(context)) {
try {
Class.forName("org.junit.runner.Runner");
wasTestRun = 1;
return true;
} catch (ClassNotFoundException ignored) {
}
}
wasTestRun = 0;
return false;
}
public static boolean isDebuggable(#Nullable Context context) {
return context != null && (context.getApplicationContext()
.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
Note that I am not using any AtomicBoolean or other helpers, as it is already pretty fast (and locking may just bring the speed down).
You can try this
if (isRunningTest == null) {
isRunningTest = false;
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
List<StackTraceElement> list = Arrays.asList(stackTrace);
for (StackTraceElement element : list) {
if (element.getClassName().startsWith("androidx.test.runner.MonitoringInstrumentation")) {
isRunningTest = true;
break;
}
}
}
This work for me because no actual device is running
public static boolean isUnitTest() {
return Build.BRAND.startsWith(Build.UNKNOWN) && Build.DEVICE.startsWith(Build.UNKNOWN) && Build.DEVICE.startsWith(Build.UNKNOWN) && Build.PRODUCT.startsWith(Build.UNKNOWN);
}