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.test.client;
18
19import static org.junit.Assert.assertTrue;
20
21import android.os.Bundle;
22import android.os.Handler;
23import android.os.Looper;
24
25import androidx.core.util.ObjectsCompat;
26import androidx.media.DataSourceDesc;
27import androidx.media.MediaItem2;
28import androidx.media.MediaMetadata2;
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    // Temporaily commenting out, since we don't have the Mock services yet.
44//    /**
45//     * Finds the session with id in this test package.
46//     *
47//     * @param context
48//     * @param id
49//     * @return
50//     */
51//    public static SessionToken2 getServiceToken(Context context, String id) {
52//        switch (id) {
53//            case MockMediaSessionService2.ID:
54//                return new SessionToken2(context, new ComponentName(
55//                        context.getPackageName(), MockMediaSessionService2.class.getName()));
56//            case MockMediaLibraryService2.ID:
57//                return new SessionToken2(context, new ComponentName(
58//                        context.getPackageName(), MockMediaLibraryService2.class.getName()));
59//        }
60//        fail("Unknown id=" + id);
61//        return null;
62//    }
63
64    /**
65     * Compares contents of two bundles.
66     *
67     * @param a a bundle
68     * @param b another bundle
69     * @return {@code true} if two bundles are the same. {@code false} otherwise. This may be
70     *     incorrect if any bundle contains a bundle.
71     */
72    public static boolean equals(Bundle a, Bundle b) {
73        return contains(a, b) && contains(b, a);
74    }
75
76    /**
77     * Checks whether a Bundle contains another bundle.
78     *
79     * @param a a bundle
80     * @param b another bundle
81     * @return {@code true} if a contains b. {@code false} otherwise. This may be incorrect if any
82     *      bundle contains a bundle.
83     */
84    public static boolean contains(Bundle a, Bundle b) {
85        if (a == b) {
86            return true;
87        }
88        if (a == null || b == null) {
89            return b == null;
90        }
91        if (!a.keySet().containsAll(b.keySet())) {
92            return false;
93        }
94        for (String key : b.keySet()) {
95            if (!ObjectsCompat.equals(a.get(key), b.get(key))) {
96                return false;
97            }
98        }
99        return true;
100    }
101
102    /**
103     * Create a playlist for testing purpose
104     * <p>
105     * Caller's method name will be used for prefix of each media item's media id.
106     *
107     * @param size list size
108     * @return the newly created playlist
109     */
110    public static List<MediaItem2> createPlaylist(int size) {
111        final List<MediaItem2> list = new ArrayList<>();
112        String caller = Thread.currentThread().getStackTrace()[1].getMethodName();
113        for (int i = 0; i < size; i++) {
114            list.add(new MediaItem2.Builder(MediaItem2.FLAG_PLAYABLE)
115                    .setMediaId(caller + "_item_" + (size + 1))
116                    .setDataSourceDesc(createDSD()).build());
117        }
118        return list;
119    }
120
121    /**
122     * Create a media item with the metadata for testing purpose.
123     *
124     * @return the newly created media item
125     * @see #createMetadata()
126     */
127    public static MediaItem2 createMediaItemWithMetadata() {
128        return new MediaItem2.Builder(MediaItem2.FLAG_PLAYABLE)
129                .setMetadata(createMetadata()).setDataSourceDesc(createDSD()).build();
130    }
131
132    /**
133     * Create a media metadata for testing purpose.
134     * <p>
135     * Caller's method name will be used for the media id.
136     *
137     * @return the newly created media item
138     */
139    public static MediaMetadata2 createMetadata() {
140        String mediaId = Thread.currentThread().getStackTrace()[1].getMethodName();
141        return new MediaMetadata2.Builder()
142                .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId).build();
143    }
144
145    private static DataSourceDesc createDSD() {
146        return new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
147    }
148
149    /**
150     * Create a bundle for testing purpose.
151     *
152     * @return the newly created bundle.
153     */
154    public static Bundle createTestBundle() {
155        Bundle bundle = new Bundle();
156        bundle.putString("test_key", "test_value");
157        return bundle;
158    }
159
160    /**
161     * Handler that always waits until the Runnable finishes.
162     */
163    public static class SyncHandler extends Handler {
164        public SyncHandler(Looper looper) {
165            super(looper);
166        }
167
168        public void postAndSync(final Runnable runnable) throws InterruptedException {
169            if (getLooper() == Looper.myLooper()) {
170                runnable.run();
171            } else {
172                final CountDownLatch latch = new CountDownLatch(1);
173                post(new Runnable() {
174                    @Override
175                    public void run() {
176                        runnable.run();
177                        latch.countDown();
178                    }
179                });
180                assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
181            }
182        }
183    }
184}
185