Skip to content
Adnan Begovic edited this page Mar 25, 2016 · 11 revisions

Testing Best Practices

Viewing test coverage

Within the CM build system, you can manually generate code coverage reports by leveraging the CTS api coverage mechanism. This allows you to view the current test coverage within the cmsdk's test package for all public interfaces that are defined in the api stubs.

To invoke, make the target for cmsdk test coverage from the root of the tree:

make cmsdk-test-coverage

The resulting html page will be within $OUT/host/<host arch>/cmsdk-api-coverage as test-coverage.html

Parceling

Since we need to maintain compatibility with respect to variances in CMSDK vs CM Framework versions, and we can't rely on runtime stubs like AOSP, we've adopted a "header" for any parcels which are sent over IPC. This header is fairly straightforward and is generously borrowed from the implementation in DashClock.

Since we rely so heavily on making sure that custom objects can pass over IPC correctly, our testing paradigms needs to enforce this.

A quick example is shown (from):

@SmallTest
public void testCustomTileOnSettingsClickIntentUnravelFromParcel() {
    Intent intent = new Intent(mContext, DummySettings.class);
    CustomTile expectedCustomTile = new CustomTile.Builder(mContext)
        .setOnSettingsClickIntent(intent)
        .build();

    // Write to parcel
    Parcel parcel = Parcel.obtain();
    expectedCustomTile.writeToParcel(parcel, 0);

    // Rewind
    parcel.setDataPosition(0);

    // Verify data when unraveling
    CustomTile fromParcel = CustomTile.CREATOR.createFromParcel(parcel);

    assertNotNull(fromParcel.onSettingsClick);
    assertEquals(expectedCustomTile.onSettingsClick.toString(),
            fromParcel.onSettingsClick.toString());
}

As you can see, we populate the members we want to test when sending over IPC. Instead of directly sending the parcel over IPC, and mocking out way too many things, we cheat a bit. Take the custom object, write it to a parcel, rewind the parcel to its original data position and leverage the custom objects CREATOR to recreate a new Object for comparison.

Common Builder Pattern

The common builder pattern is tested mostly the same way (we're essentially just testing that the builders .build() invocation actually gives us the data we want.

@SmallTest
public void testCustomTileBuilderOnClickIntent() {
    Intent intent = new Intent(Intent.ACTION_DIAL);
    PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
    CustomTile customTile = new CustomTile.Builder(mContext)
        .setOnClickIntent(pendingIntent)
        .build();
    assertNotNull(customTile.onClick);
    assertEquals(pendingIntent, customTile.onClick);
}

The above assertion simply tests that the pending intent we're passing in actually comes out of the built custom tile object as expected.

Verifying your service

The simplest test is to verify that your service is set up correctly and will be exposed to third parties during runtime on CyanogenMod.

A brief example here:

@SmallTest
public void testManagerServiceIsAvailable() {
    ICMStatusBarManager icmStatusBarManager = mCMStatusBarManager.getService();
    assertNotNull(icmStatusBarManager);
}

The above test does a simple call to the CMStatusBarManager to retrieve its bound service, stores it within the AIDL where the interface is defined, and verifies that the object isn't null.

Clone this wiki locally