1/*
2 * Copyright (C) 2015 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 android.location;
18
19import junit.framework.TestCase;
20
21import android.test.suitebuilder.annotation.SmallTest;
22
23import java.lang.reflect.Constructor;
24import java.lang.reflect.Method;
25import java.util.HashSet;
26import java.util.Iterator;
27import java.util.Random;
28import java.util.Set;
29
30/**
31 * Unit tests for {@link GpsStatus}.
32 */
33@SmallTest
34public class GpsStatusTest extends TestCase {
35
36    private static final int MAX_VALUE = 250;
37
38    private final Random mRandom = new Random();
39
40    private GpsStatus mStatus;
41    private int mCount;
42    private int[] mPrns;
43    private float[] mCn0s;
44    private float[] mElevations;
45    private float[] mAzimuth;
46    private int mEphemerisMask;
47    private int mAlmanacMask;
48    private int mUsedInFixMask;
49
50    public void setUp() throws Exception {
51        super.setUp();
52        mStatus = createGpsStatus();
53        generateSatellitesData(generateInt());
54    }
55
56    public void testEmptyGpsStatus() throws Exception {
57        verifyIsEmpty(mStatus);
58    }
59
60    public void testGpsStatusIterator() throws Exception {
61        generateSatellitesData(2);
62        setSatellites(mStatus);
63        Iterator<GpsSatellite> iterator = mStatus.getSatellites().iterator();
64        assertTrue("hasNext(1)", iterator.hasNext());
65        assertTrue("hasNext(1) does not overflow", iterator.hasNext());
66        GpsSatellite satellite1 = iterator.next();
67        assertNotNull("satellite", satellite1);
68        assertTrue("hasNext(2)", iterator.hasNext());
69        assertTrue("hasNext(2) does not overflow", iterator.hasNext());
70        GpsSatellite satellite2 = iterator.next();
71        assertNotNull("satellite", satellite2);
72        assertFalse("hasNext() no elements", iterator.hasNext());
73    }
74
75    public void testTtff() throws Exception {
76        int testTtff = generateInt();
77        set(mStatus, testTtff);
78        verifyTtff(mStatus, testTtff);
79    }
80
81    public void testCopyTtff() throws Exception {
82        int testTtff = generateInt();
83        verifyTtff(mStatus, 0);
84
85        GpsStatus otherStatus = createGpsStatus();
86        set(otherStatus, testTtff);
87        verifyTtff(otherStatus, testTtff);
88
89        set(mStatus, otherStatus);
90        verifyTtff(mStatus, testTtff);
91    }
92
93    public void testSetSatellites() throws Exception {
94        setSatellites(mStatus);
95        verifySatellites(mStatus);
96    }
97
98    public void testCopySatellites() throws Exception {
99        verifyIsEmpty(mStatus);
100
101        GpsStatus otherStatus = createGpsStatus();
102        setSatellites(otherStatus);
103        verifySatellites(otherStatus);
104
105        set(mStatus, otherStatus);
106        verifySatellites(mStatus);
107    }
108
109    public void testOverrideSatellites() throws Exception {
110        setSatellites(mStatus);
111        verifySatellites(mStatus);
112
113        GpsStatus otherStatus = createGpsStatus();
114        generateSatellitesData(mCount, true /* reusePrns */);
115        setSatellites(otherStatus);
116        verifySatellites(otherStatus);
117
118        set(mStatus, otherStatus);
119        verifySatellites(mStatus);
120    }
121
122    public void testAddSatellites() throws Exception {
123        int count = 10;
124        generateSatellitesData(count);
125        setSatellites(mStatus);
126        verifySatellites(mStatus);
127
128        GpsStatus otherStatus = createGpsStatus();
129        generateSatellitesData(count);
130        setSatellites(otherStatus);
131        verifySatellites(otherStatus);
132
133        set(mStatus, otherStatus);
134        verifySatellites(mStatus);
135    }
136
137    public void testAddMoreSatellites() throws Exception {
138        int count = 25;
139        generateSatellitesData(count);
140        setSatellites(mStatus);
141        verifySatellites(mStatus);
142
143        GpsStatus otherStatus = createGpsStatus();
144        generateSatellitesData(count * 2);
145        setSatellites(otherStatus);
146        verifySatellites(otherStatus);
147
148        set(mStatus, otherStatus);
149        verifySatellites(mStatus);
150    }
151
152    public void testAddLessSatellites() throws Exception {
153        int count = 25;
154        generateSatellitesData(count * 2);
155        setSatellites(mStatus);
156        verifySatellites(mStatus);
157
158        GpsStatus otherStatus = createGpsStatus();
159        generateSatellitesData(count);
160        setSatellites(otherStatus);
161        verifySatellites(otherStatus);
162
163        set(mStatus, otherStatus);
164        verifySatellites(mStatus);
165    }
166
167    private static void verifyIsEmpty(GpsStatus status) {
168        verifySatelliteCount(status, 0);
169        verifyTtff(status, 0);
170    }
171
172    private static void verifySatelliteCount(GpsStatus status, int expectedCount) {
173        int satellites = 0;
174        for (GpsSatellite s : status.getSatellites()) {
175            ++satellites;
176        }
177        assertEquals("GpsStatus::SatelliteCount", expectedCount, satellites);
178    }
179
180    private void verifySatellites(GpsStatus status) {
181        verifySatelliteCount(status, mCount);
182        verifySatellites(status, mCount, mPrns, mCn0s, mElevations, mAzimuth, mEphemerisMask,
183                mAlmanacMask, mUsedInFixMask);
184    }
185
186    private static void verifySatellites(
187            GpsStatus status,
188            int count,
189            int[] prns,
190            float[] cn0s,
191            float[] elevations,
192            float[] azimuth,
193            int ephemerisMask,
194            int almanacMask,
195            int usedInFixMask) {
196        for (int i = 0; i < count; ++i) {
197            int prn = prns[i];
198            GpsSatellite satellite = getSatellite(status, prn);
199            assertNotNull(getSatelliteAssertInfo(i, prn, "non-null"), satellite);
200            assertEquals(getSatelliteAssertInfo(i, prn, "Snr"), cn0s[i], satellite.getSnr());
201            assertEquals(
202                    getSatelliteAssertInfo(i, prn, "Elevation"),
203                    elevations[i],
204                    satellite.getElevation());
205            assertEquals(
206                    getSatelliteAssertInfo(i, prn, "Azimuth"),
207                    azimuth[i],
208                    satellite.getAzimuth());
209            int prnShift = 1 << (prn - 1);
210            assertEquals(
211                    getSatelliteAssertInfo(i, prn, "ephemeris"),
212                    (ephemerisMask & prnShift) != 0,
213                    satellite.hasEphemeris());
214            assertEquals(
215                    getSatelliteAssertInfo(i, prn, "almanac"),
216                    (almanacMask & prnShift) != 0,
217                    satellite.hasAlmanac());
218            assertEquals(
219                    getSatelliteAssertInfo(i, prn, "usedInFix"),
220                    (usedInFixMask & prnShift) != 0,
221                    satellite.usedInFix());
222        }
223    }
224
225    private static void verifyTtff(GpsStatus status, int expectedTtff) {
226        assertEquals("GpsStatus::TTFF", expectedTtff, status.getTimeToFirstFix());
227    }
228
229    private static GpsStatus createGpsStatus() throws Exception {
230        Constructor<GpsStatus>  ctor = GpsStatus.class.getDeclaredConstructor();
231        ctor.setAccessible(true);
232        return ctor.newInstance();
233    }
234
235    private static void set(GpsStatus status, int ttff) throws Exception {
236        Class<?> statusClass = status.getClass();
237        Method setTtff = statusClass.getDeclaredMethod("setTimeToFirstFix", Integer.TYPE);
238        setTtff.setAccessible(true);
239        setTtff.invoke(status, ttff);
240    }
241
242    private static void set(GpsStatus status, GpsStatus statusToSet) throws Exception {
243        Class<?> statusClass = status.getClass();
244        Method setStatus = statusClass.getDeclaredMethod("setStatus", statusClass);
245        setStatus.setAccessible(true);
246        setStatus.invoke(status, statusToSet);
247    }
248
249    private void setSatellites(GpsStatus status) throws Exception {
250        set(status, mCount, mPrns, mCn0s, mElevations, mAzimuth, mEphemerisMask, mAlmanacMask,
251                mUsedInFixMask);
252    }
253
254    private static void set(
255            GpsStatus status,
256            int count,
257            int[] prns,
258            float[] cn0s,
259            float[] elevations,
260            float[] azimuth,
261            int ephemerisMask,
262            int almanacMask,
263            int usedInFixMask) throws Exception {
264        Class<?> statusClass = status.getClass();
265        Class<?> intClass = Integer.TYPE;
266        Class<?> floatArrayClass = Class.forName("[F");
267        Method setStatus = statusClass.getDeclaredMethod(
268                "setStatus",
269                intClass,
270                Class.forName("[I"),
271                floatArrayClass,
272                floatArrayClass,
273                floatArrayClass,
274                intClass,
275                intClass,
276                intClass);
277        setStatus.setAccessible(true);
278        setStatus.invoke(
279                status,
280                count,
281                prns,
282                cn0s,
283                elevations,
284                azimuth,
285                ephemerisMask,
286                almanacMask,
287                usedInFixMask);
288    }
289
290    private int generateInt() {
291        return mRandom.nextInt(MAX_VALUE) + 1;
292    }
293
294    private int[] generateIntArray(int count) {
295        Set<Integer> generatedPrns = new HashSet<>();
296        int[] array = new int[count];
297        for(int i = 0; i < count; ++i) {
298            int generated;
299            do {
300                generated = generateInt();
301            } while (generatedPrns.contains(generated));
302            array[i] = generated;
303            generatedPrns.add(generated);
304        }
305        return array;
306    }
307
308    private float[] generateFloatArray(int count) {
309        float[] array = new float[count];
310        for(int i = 0; i < count; ++i) {
311            array[i] = generateInt();
312        }
313        return array;
314    }
315
316    private int generateMask(int[] prns) {
317        int mask = 0;
318        int prnsLength = prns.length;
319        for (int i = 0; i < prnsLength; ++i) {
320            if (mRandom.nextBoolean()) {
321                mask |= 1 << (prns[i] - 1);
322            }
323        }
324        return mask;
325    }
326
327    private void generateSatellitesData(int count) {
328        generateSatellitesData(count, false /* reusePrns */);
329    }
330
331    private void generateSatellitesData(int count, boolean reusePrns) {
332        mCount = count;
333        if (!reusePrns) {
334            mPrns = generateIntArray(count);
335        }
336        mCn0s = generateFloatArray(count);
337        mElevations = generateFloatArray(count);
338        mAzimuth = generateFloatArray(count);
339        mEphemerisMask = generateMask(mPrns);
340        mAlmanacMask = generateMask(mPrns);
341        mUsedInFixMask = generateMask(mPrns);
342    }
343
344    private static GpsSatellite getSatellite(GpsStatus status, int prn) {
345        for (GpsSatellite satellite : status.getSatellites()) {
346            if (satellite.getPrn() == prn) {
347                return satellite;
348            }
349        }
350        return null;
351    }
352
353    private static String getSatelliteAssertInfo(int index, int prn, String param) {
354        return String.format("Satellite::%s [i=%d, prn=%d]", param, index, prn);
355    }
356}
357