[Android]Relax “Restrictions on non-SDK interfaces” on Android P

Android P has a feature of Restrictions on non-SDK interfaces.
=> https://developer.android.com/preview/restrictions-non-sdk-interfaces

If you have a method like below, you will face java.lang.NoSuchMethodException: setAnimationScales [class [F] error. This is because of the non-sdk interface feature.

    private fun animationHandler() {
        try {
            val asInterface = Class.forName("android.view.IWindowManager\$Stub").getDeclaredMethod("asInterface", IBinder::class.java)

            val getService = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", String::class.java)

            val windowManagerClazz = Class.forName("android.view.IWindowManager")

            setAnimationScalesMethod = Class.forName("android.view.IWindowManager").getDeclaredMethod("setAnimationScales", FloatArray::class.java)
            getAnimationScalesMethod = windowManagerClazz.getDeclaredMethod("getAnimationScales")
            windowManagerObject = asInterface.invoke(null, getService.invoke(null, "window") as IBinder)
        } catch (e: Exception) {
            throw RuntimeException("Failed to access animation methods", e)
        }
    }

https://github.com/KazuCocoa/DroidTestHelper/blob/3781fa40a6c9dfd34e1cb94ae230a98931af3313/droidtesthelperlib/src/main/java/com/kazucocoa/droidtesthelperlib/HandleAnimations.kt#L23

To avoid the error, we can call below adb commands.

adb shell settings put global hidden_api_policy_pre_p_apps  1
adb shell settings put global hidden_api_policy_p_apps 1

After this, calling setAnimationScales succeeds.
I’ve appended the note in https://github.com/KazuCocoa/DroidTestHelper#note-for-android-p .

BTW, we can get P image from https://dl.google.com/android/repository/sys-img/google_apis_playstore/x86-P_r02.zip . After downloading it and putting it in /Users/username/Library/Android/sdk/system-images/android-P/android_apis_playstore, you can create Android P image via AVDManager.

Advertisements

[Android]run tests on multiple emulators

I published x3 Speed Up Android CI at Cookpad last week.

I’d like to leave the same thing in https://github.com/KazuCocoa/EspressoEnv/blob/master/script/run_instrumented_tests.sh

The script is almost the same, but it has composer script as well.

[Android][Espresso] Decrease test failure because of ARN dialog

When I ran android tests on OS 8.1 emulators, I faced some ARN dialog by Google bundled apps. They led test failure related with UI, Espresso, because of the dialog remained on the UI.

To reduce the error, we can implement like below code in TestRunner.

class BaseAndroidJUnitRunner : AndroidJUnitRunner() {
    override fun onStart() {
        closeAppIfGoogleAppShowsARN()
        super.onStart()
    }

    private fun closeAppIfGoogleAppShowsARN() {
        val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
        uiDevice.findObject(By.res("android:id/aerr_close"))?.let {
            it.click()
            uiDevice.wait(Until.gone(By.res("android:id/aerr_close")), 500)
        }
        uiDevice.findObject(By.res("android:id/aerr_mute"))?.let {
            it.click()
            uiDevice.wait(Until.gone(By.res("android:id/aerr_mute")), 500)
        }
    }
}

https://github.com/KazuCocoa/EspressoEnv/pull/8/files

If the dialog appears before running tests, we can close it.

I could see the id resource over OS 7.0. So, We probably should implement another way for under OS 7.0. But i haven’t seen the dialog under 7.0 recently.

[iOS][Appium]How to debug, set break points, WebDriverAgent

Debugging WebDriverAgent is tricky. Appium uses the library to run tests against iOS.

In this article, I’d like to leave how to debug WebDriverAgent using Appium. You can set break points in arbitrary lines on WebDriverAgent via Xcode following below.

On Xcode

  1. Clone the repository and finish the setup
$ git clone git@github.com:appium/WebDriverAgent.git
$ cd WebDriverAgent
$ ./Scripts/bootstrap.sh
  1. Open WebDriverAgent.xcodeproj with Xcode
  2. Open Edit Scheme
  3. Set 8100 as the port
  4. Run test with iPhone 8 Plus and 11.4

Then you can see below logs in console.

Test Suite 'UITestingUITests' started at 2018-06-24 23:48:32.738
Test Case '-[UITestingUITests testRunner]' started.
    t =     0.01s Start Test at 2018-06-24 23:48:32.747
    t =     0.01s Set Up
2018-06-24 23:48:32.755802+0900 WebDriverAgentRunner-Runner[57623:1453006] Built at Jun 24 2018 23:42:12
2018-06-24 23:48:32.807755+0900 WebDriverAgentRunner-Runner[57623:1453006] ServerURLHere->http://172.254.99.34:8100<-ServerURLHere

Appium

  1. set up Appium server
$ git clone git@github.com:appium/appium.git
$ cd appium
$ npm install
$ node .

Appium client

  1. Run arbitrart appium client with below capabilities
platformName: :ios,
automationName: 'XCUITest',
app: 'path/to/test/app',
platformVersion: '11.4', # Same OS version with running simulator
deviceName: 'iPhone 8 Plus', # Same Devive name with running simulator
# useNewWDA: true, # We should disable this capability to avoid uninstalling WDA from the simulator
webDriverAgentUrl: 'http://172.254.99.34:8100' # ServerURLHere->http://172.254.99.34:8100<-ServerURLHere

In my case, I run with the capability on ruby_lib_core.

Then

We can use WDA with breaking point like iOS development.

[Android]Use PermissionRequester to get permissions

I’m using composer to handle instrumented tests.

GrantPermissionRule has introduced for the andorid test support library, [Android]Checking Android Testing Support Library 1.0. The Rule work as @Rule in JUnit4.

We’d like to clean screenshots every time. But we need some permissions to clean them. To achieve it, we can use PermissionRequester to get permissions.

In my case, I run this in Runner and before starting test cases.

fun clearComposerScreenshots() {
  val requester = PermissionRequester()
  requester.addPermissions(
    android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
    android.Manifest.permission.READ_EXTERNAL_STORAGE)
  requester.requestPermissions()
  val spoon = Environment.getExternalStoragePublicDirectory("app_spoon-screenshots")
  val deleteRecursively = spoon.deleteRecursively()
  val result = if (deleteRecursively) "success" else "failure"
  Log.i(TAG, "clearing screenshots from folder ${spoon.absolutePath} $result")
}

https://github.com/KazuCocoa/EspressoEnv/pull/5 is an example to use the above in Runner.

[iOS][Appium]Process Arguments by Appium for XCUITest, iOS

Today, I’d like to leave a tip. I’ve implemented test cases using this article for over 3 years. It helped us to control test environment more stable and fast.

XCUITest can set process arguments for Instrumentation.

According to https://github.com/appium/appium-xcuitest-driver#desired-capabilities, you can set processArguments in capability and the test app can get the value via ProcessInfo.

You can set the value via below’s Environment Variables.

Screen Shot 2018-06-10 at 20.54.14

Client

Next code is Ruby based.

IOS_OPS = {
  caps: {
    platformName: :ios,
    automationName: 'XCUITest',
    ...
    processArguments: {
      env: {
        "PROCESS_VALUE": "process value"
      }
    }
  },
  appium_lib: {
    ...
  }
}.freeze

By processArguments, you can set the environment variables.

Test app

The processArguments can get via ProcessInfo like below.

import Foundation

let env = ProcessInfo.processInfo.environment
env["PROCESS_VALUE"] // "process value"

Next image isn’t the above. But I believe it help you understand ProcessInfo.processInfo.environment.
Screen Shot 2018-06-10 at 20.15.50

You can control some behaviour using the argument. For example, URLs or interval for network.

Read “Continuous Delivery with Spinnaker”

I read Continuous Delivery with Spinnaker to catch up with Spinnaker and the bases.

Recent my profession is mobile, but I have some experience/knowledge of distributed systems since I studied the Byzantine General Problems in my university. And I also has caught up with network/tools/fundamentals associated with it. (Especially testing and monitoring topics.)

You can learn current problem an solution for continuous delivery against cloud-based complex environments with Spinnaker.

In chapter 1 to 4, you can learn the basic concept of continuous delivery and the current cloud-native world. For example, pipeline-related topics. The pipeline is development pipeline from implementation to deployment through testing.

The testing is not only development testing but also testing in production like Chaos and Canary.
We have two main triggers to kick the pipeline. One is time base, and another is event based. The time base is manual or cron. The event base is git, continuous integration, docker or pipeline.

After chapter 5, you can learn the more real usage of Spinnaker. Of course, you can also know Kubenetis since it is a part of deployment toolset in spinnaker.

From chapter 11, you can learn additional tips for Spinnaker.

This book helps you to understand current problem an solution for continuous delivery against cloud-based complex environments with Spinnaker as I addressed in the above.

For me, it’s significant excited topic about complex/chaos system related problems and how to solve them. So, I enjoyed learning the book.

[Android]some mocking features work only on Android P+

One of my colleagues told me Mockk. The library is pure Kotlin mocking library. An interesting thing for me is Android instrumentation support.

I had a question when I saw that why Android P was a limitation. Since it provide mocking feature for final classes / objects in instrumentation tests.

I investigated a bit and I found interesting issues. So I left them here for my note.

Mokito

Dexmaker

 

BTW, Dexmaker has been provided mocking final classes/methods with Mokito on Android P. We can use the feature with below.

Thanks for the fixing and improvements.

Create a PR to update outdated libraries by script

Updating outdated libraries is important. Longtime outdated libraries probably lead difficult updating libraries. Sometimes they have breaking changes. We can reduce the risk updating libraries frequent and keep them small size.

Meanwhile, it’s difficult to catch up with ALL outdated libraries and updating them frequently. Since our works aren’t only updating them.

Get outdated libraries by anticuado

I’ve created https://github.com/KazuCocoa/anticuado to help get outdated libraries. Using the library, you can get outdated libraries as JSON format. Currently, the library supports the below.

  • Java
    • Gradle
  • iOS
    • CocoaPods
    • Carthage
  • Ruby
    • Bundler
  • Elixir
    • Hex
  • JavaScript
    • npm
    • yarn

How to use it

For example, you can run it against cocoapods.

require "anticuado"

cocoadpos = ::Anticuado::IOS::CocoaPods.new "path/to/project"
outdated = cocoadpos.outdated 
cocoadpos.format outdated

The output is the below.

[
  {
    library_name: "AFNetworking",
    current_version: "2.5.0",
    available_version: "3.1.0",
    latest_version: "3.1.0"
  },
  {
    library_name: "OHHTTPStubs",
    current_version: "4.1.0",
    available_version: "5.0.0",
    latest_version: "5.0.0"
  }
]

You can run it on CI services like Jenkins and send them to your slack channels.

Create a new PR automatically

Lately, I’ve added a feature to create a PR which include updating outdated libraries. The below is an example of cocoapods. The target repository is https://github.com/KazuCocoa/test.example. The result is https://github.com/KazuCocoa/test.examples/pull/2

If you have CI environment, automated tests should run after creating the PR. If they are green, you probably can merge it to master branch.

As a result, you can reduce the work to update outdated libraries by yourselves.

conclusion

The update logic is not complicated, but the tasks drain our time. This kind of work will improve your development environment step by step, I believe.