Создание уведомления от InstrumentationTestCase

Я хочу проверить из unit test, может ли уведомление воспроизводить пользовательский звук из активов. Тест не предназначен для проверки чего-либо, я написал его как быстрый способ продемонстрировать функцию без загромождения основного кода приложения.

Итак, в тестовом проекте я добавил wav файл внутри /res/raw. Я буду использовать этот URL с создателем уведомлений:

Uri path = Uri.parse("android.resource://<main app package name>/testsound.wav");

Этот URL должен работать в соответствии с вопросами, которые я читал в SO. Предположим, что это работает.

Теперь, поскольку я не хотел включать тестовый wav файл в основную папку проекта /res/raw, но в тестовом проекте один, я вынужден сделать мой unit test продолжением от InstrumentationTestCase, чтобы я мог доступ к ресурсам в тестовом проекте.

Здесь код:

    NotificationCompat.Builder builder = new NotificationCompat.Builder(getInstrumentation().getContext());
    ...
    builder.setSound(path, AudioManager.STREAM_NOTIFICATION);
    ...
    NotificationManager notificationManager = (NotificationManager) getInstrumentation().getContext().getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(NOTIFICATION_ID, builder.build());

Вызов notify вызывает следующее исключение:

    java.lang.SecurityException: Calling uid 10198 gave package <main app package name> which is owned by uid 10199
    at android.os.Parcel.readException(Parcel.java:1540)
    at android.os.Parcel.readException(Parcel.java:1493)
    at android.app.INotificationManager$Stub$Proxy.enqueueNotificationWithTag(INotificationManager.java:611)
    at android.app.NotificationManager.notify(NotificationManager.java:187)
    at android.app.NotificationManager.notify(NotificationManager.java:140)
    ... 
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1873)

Я отслеживал это исключение до класса NotificationManagerService:

    void checkCallerIsSystemOrSameApp(String pkg) {
        int uid = Binder.getCallingUid();
        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
            return;
        }
        try {
            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
                    pkg, 0, UserHandle.getCallingUserId());
            if (!UserHandle.isSameApp(ai.uid, uid)) {
                throw new SecurityException("Calling uid " + uid + " gave package"
                        + pkg + " which is owned by uid " + ai.uid);
            }
        } catch (RemoteException re) {
            throw new SecurityException("Unknown package " + pkg + "\n" + re);
        }
    }

По-видимому, исключение не имеет ничего общего с пользовательским звуком, но с тем фактом, что мы создаем уведомление от InstrumentationTestCase.

Есть ли способ проверить это? Я помню, что создавал уведомления из AndroidTestCase в прошлом, но если я это сделаю, то я не смогу получить доступ к тестовому wav файлу. Я мог бы создать банку с wav и сбросить банку в папку тестового проекта lib, но это будет скрывать файл, а другим программистам может быть трудно найти его, если им нужно будет заменить его в будущем.

Ответ 1

Я понял, как заставить звук работать с AndroidTestCase. WAV файл был добавлен в исходную папку тестового проекта и не был включен в основной проект, как предполагалось.

URL-адрес уведомления был следующим:

Uri path = Uri.parse("android.resource://<test project package name>/" + R.raw.air_horn);

И строитель был получен следующим образом:

NotificationCompat.Builder builder = new NotificationCompat.Builder(getContext());

Ответ 2

Собственно, меня немного озадачил вопрос. Поэтому я написал небольшой тест Instrumentation.

Для утверждения я пометил тест для запуска только на API 23 (getActiveNotifications, кажется, недоступен раньше), но он отлично работает и с более старыми API-интерфейсами.

Трюк заключается в использовании getTargetContext() вместо getContext():

public final class MainActivityTest extends ActivityUnitTestCase<MainActivity> {

    public MainActivityTest() {
        super(MainActivity.class);
    }

    @TargetApi(Build.VERSION_CODES.M)
    public void testSendNotification() {
        final NotificationManager manager = (NotificationManager) getInstrumentation().getTargetContext().getSystemService(Context.NOTIFICATION_SERVICE);

        manager.cancel(42);
        assertEquals(0, manager.getActiveNotifications().length);

        final NotificationCompat.Builder builder = new NotificationCompat.Builder(getInstrumentation().getTargetContext());
        builder.setContentTitle("Notification test")
                .setAutoCancel(true)
                .setContentText("Hello, Mister Smith")
                .setSmallIcon(R.drawable.ic_launcher_notification);

        manager.notify(42, builder.build());

        assertEquals(1, manager.getActiveNotifications().length);
    }
}

Он работает как шарм:

введите описание изображения здесь

Надеюсь, это поможет.