1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.media;
18
19import static org.junit.Assert.assertTrue;
20import static org.junit.Assert.fail;
21
22import android.content.ComponentName;
23import android.content.Context;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.Looper;
27
28import androidx.core.util.ObjectsCompat;
29
30import java.io.FileDescriptor;
31import java.util.ArrayList;
32import java.util.List;
33import java.util.concurrent.CountDownLatch;
34import java.util.concurrent.TimeUnit;
35
36/**
37 * Utilities for tests.
38 */
39public final class TestUtils {
40    private static final int WAIT_TIME_MS = 1000;
41    private static final int WAIT_SERVICE_TIME_MS = 5000;
42
43    /**
44     * Finds the session with id in this test package.
45     *
46     * @param context
47     * @param id
48     * @return
49     */
50    public static SessionToken2 getServiceToken(Context context, String id) {
51        switch (id) {
52            case MockMediaSessionService2.ID:
53                return new SessionToken2(context, new ComponentName(
54                        context.getPackageName(), MockMediaSessionService2.class.getName()));
55            case MockMediaLibraryService2.ID:
56                return new SessionToken2(context, new ComponentName(
57                        context.getPackageName(), MockMediaLibraryService2.class.getName()));
58        }
59        fail("Unknown id=" + id);
60        return null;
61    }
62
63    /**
64     * Compares contents of two bundles.
65     *
66     * @param a a bundle
67     * @param b another bundle
68     * @return {@code true} if two bundles are the same. {@code false} otherwise. This may be
69     *     incorrect if any bundle contains a bundle.
70     */
71    public static boolean equals(Bundle a, Bundle b) {
72        return contains(a, b) && contains(b, a);
73    }
74
75    /**
76     * Checks whether a Bundle contains another bundle.
77     *
78     * @param a a bundle
79     * @param b another bundle
80     * @return {@code true} if a contains b. {@code false} otherwise. This may be incorrect if any
81     *      bundle contains a bundle.
82     */
83    public static boolean contains(Bundle a, Bundle b) {
84        if (a == b) {
85            return true;
86        }
87        if (a == null || b == null) {
88            return b == null;
89        }
90        if (!a.keySet().containsAll(b.keySet())) {
91            return false;
92        }
93        for (String key : b.keySet()) {
94            if (!ObjectsCompat.equals(a.get(key), b.get(key))) {
95                return false;
96            }
97        }
98        return true;
99    }
100
101    /**
102     * Create a playlist for testing purpose
103     * <p>
104     * Caller's method name will be used for prefix of each media item's media id.
105     *
106     * @param size list size
107     * @return the newly created playlist
108     */
109    public static List<MediaItem2> createPlaylist(int size) {
110        final List<MediaItem2> list = new ArrayList<>();
111        String caller = Thread.currentThread().getStackTrace()[1].getMethodName();
112        for (int i = 0; i < size; i++) {
113            list.add(new MediaItem2.Builder(MediaItem2.FLAG_PLAYABLE)
114                    .setMediaId(caller + "_item_" + (size + 1))
115                    .setDataSourceDesc(createDSD()).build());
116        }
117        return list;
118    }
119
120    /**
121     * Create a media item with the metadata for testing purpose.
122     *
123     * @return the newly created media item
124     * @see #createMetadata()
125     */
126    public static MediaItem2 createMediaItemWithMetadata() {
127        return new MediaItem2.Builder(MediaItem2.FLAG_PLAYABLE)
128                .setMetadata(createMetadata()).setDataSourceDesc(createDSD()).build();
129    }
130
131    /**
132     * Create a media metadata for testing purpose.
133     * <p>
134     * Caller's method name will be used for the media id.
135     *
136     * @return the newly created media item
137     */
138    public static MediaMetadata2 createMetadata() {
139        String mediaId = Thread.currentThread().getStackTrace()[1].getMethodName();
140        return new MediaMetadata2.Builder()
141                .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId).build();
142    }
143
144    private static DataSourceDesc createDSD() {
145        return new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
146    }
147
148    /**
149     * Create a bundle for testing purpose.
150     *
151     * @return the newly created bundle.
152     */
153    public static Bundle createTestBundle() {
154        Bundle bundle = new Bundle();
155        bundle.putString("test_key", "test_value");
156        return bundle;
157    }
158
159    /**
160     * Handler that always waits until the Runnable finishes.
161     */
162    public static class SyncHandler extends Handler {
163        public SyncHandler(Looper looper) {
164            super(looper);
165        }
166
167        public void postAndSync(final Runnable runnable) throws InterruptedException {
168            if (getLooper() == Looper.myLooper()) {
169                runnable.run();
170            } else {
171                final CountDownLatch latch = new CountDownLatch(1);
172                post(new Runnable() {
173                    @Override
174                    public void run() {
175                        runnable.run();
176                        latch.countDown();
177                    }
178                });
179                assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
180            }
181        }
182    }
183}
184