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 */
16package com.android.car.vehiclenetwork.libtest;
17
18import android.os.HandlerThread;
19import android.os.SystemClock;
20import android.test.AndroidTestCase;
21import android.test.suitebuilder.annotation.MediumTest;
22import android.util.ArraySet;
23import android.util.Log;
24
25import com.android.car.vehiclenetwork.VehicleNetwork;
26import com.android.car.vehiclenetwork.VehicleNetwork.VehicleNetworkListener;
27import com.android.car.vehiclenetwork.VehicleNetworkConsts;
28import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropAccess;
29import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropChangeMode;
30import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType;
31import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
32import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs;
33import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
34import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValues;
35import com.android.car.vehiclenetwork.VehicleNetworkProtoUtil;
36
37import java.util.LinkedList;
38
39@MediumTest
40public class VehicleNetworkTest extends AndroidTestCase {
41    private static final String TAG = VehicleNetworkTest.class.getSimpleName();
42    private static final long TIMEOUT_MS = 2000;
43
44    private final HandlerThread mHandlerThread = new HandlerThread(
45            VehicleNetworkTest.class.getSimpleName());
46    private VehicleNetwork mVehicleNetwork;
47    private final TestListener mListener = new TestListener();
48
49    @Override
50    protected void setUp() throws Exception {
51        super.setUp();
52        mHandlerThread.start();
53        mVehicleNetwork = VehicleNetwork.createVehicleNetwork(mListener,
54                mHandlerThread.getLooper());
55    }
56
57    @Override
58    protected void tearDown() throws Exception {
59        super.tearDown();
60        mHandlerThread.quit();
61    }
62
63    public void testListProperties() {
64        VehiclePropConfigs configs = mVehicleNetwork.listProperties();
65        assertNotNull(configs);
66        assertTrue(configs.getConfigsCount() > 0);
67        Log.i(TAG, "got configs:" + configs.getConfigsCount());
68        for (VehiclePropConfig config : configs.getConfigsList()) {
69            Log.i(TAG, VehicleNetworkProtoUtil.VehiclePropConfigToString(config));
70        }
71
72        int oneProperty = configs.getConfigs(0).getProp();
73        VehiclePropConfigs configs2 = mVehicleNetwork.listProperties(oneProperty);
74        assertEquals(1, configs2.getConfigsCount());
75        assertTrue(VehicleNetworkProtoUtil.VehiclePropConfigEquals(
76                configs.getConfigs(0), configs2.getConfigs(0)));
77
78        VehiclePropConfigs configs3 = mVehicleNetwork.listProperties(-1);
79        assertNull(configs3);
80    }
81
82    public void testGetProperty() {
83        try {
84            VehiclePropValue value = mVehicleNetwork.getProperty(-1);
85            fail();
86        } catch (IllegalArgumentException e) {
87            // expected
88        }
89        VehiclePropConfigs configs = mVehicleNetwork.listProperties();
90        assertNotNull(configs);
91        assertTrue(configs.getConfigsCount() > 0);
92        Log.i(TAG, "got configs:" + configs.getConfigsCount());
93        for (VehiclePropConfig config : configs.getConfigsList()) {
94            if ((config.getAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_READ) != 0) {
95                if (config.getProp() == VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET) {
96                    continue;
97                }
98                if (config.getProp() >= VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_START &&
99                        config.getProp() <= VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_END) {
100                    // internal property requires write to read
101                    VehiclePropValue v = VehicleNetworkTestUtil.createDummyValue(config.getProp(),
102                            config.getValueType());
103                    mVehicleNetwork.setProperty(v);
104                }
105                VehiclePropValue value = mVehicleNetwork.getProperty(config.getProp());
106                assertEquals(config.getProp(), value.getProp());
107                assertEquals(config.getValueType(), value.getValueType());
108                Log.i(TAG, " got property:" +
109                        VehicleNetworkProtoUtil.VehiclePropValueToString(value));
110            }
111        }
112    }
113
114    public void testSetProperty() {
115        try {
116            VehiclePropValue value = VehiclePropValue.newBuilder().
117                setProp(-1).
118                setValueType(VehicleValueType.VEHICLE_VALUE_TYPE_INT32).
119                addInt32Values(0).
120                build();
121            mVehicleNetwork.setProperty(value);
122            fail();
123        } catch (SecurityException e) {
124            // expected
125        }
126        VehiclePropConfigs configs = mVehicleNetwork.listProperties();
127        assertNotNull(configs);
128        assertTrue(configs.getConfigsCount() > 0);
129        Log.i(TAG, "got configs:" + configs.getConfigsCount());
130        for (VehiclePropConfig config : configs.getConfigsList()) {
131            if ((config.getAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE) != 0) {
132                if (config.getValueType() == VehicleValueType.VEHICLE_VALUE_TYPE_INT32) {
133                    VehiclePropValue value = VehiclePropValue.newBuilder().
134                            setProp(config.getProp()).
135                            setValueType(config.getValueType()).
136                            addInt32Values(0).
137                            build();
138                    mVehicleNetwork.setProperty(value);
139                }
140            } else {
141                try {
142                    VehiclePropValue value = VehiclePropValue.newBuilder().
143                            setProp(config.getProp()).
144                            setValueType(config.getValueType()).
145                            build();
146                    mVehicleNetwork.setProperty(value);
147                    fail();
148                } catch (IllegalArgumentException e) {
149                    // expected
150                }
151            }
152        }
153    }
154
155    public void testSubscribe() throws Exception {
156        try {
157            mVehicleNetwork.subscribe(-1, 0);
158            fail();
159        } catch (SecurityException e) {
160            //expected
161        }
162        VehiclePropConfigs configs = mVehicleNetwork.listProperties();
163        assertNotNull(configs);
164        assertTrue(configs.getConfigsCount() > 0);
165        Log.i(TAG, "got configs:" + configs.getConfigsCount());
166        LinkedList<Integer> subscribedProperties = new LinkedList<Integer>();
167        for (VehiclePropConfig config : configs.getConfigsList()) {
168            if (config.getChangeMode() == VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC) {
169                // cannot subscribe
170                try {
171                    mVehicleNetwork.subscribe(config.getProp(), config.getSampleRateMin());
172                    fail();
173                } catch (IllegalArgumentException e) {
174                    //expected
175                }
176            } else if ((config.getAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_READ) != 0){
177                mVehicleNetwork.subscribe(config.getProp(), config.getSampleRateMin());
178                subscribedProperties.add(config.getProp());
179                if (config.getProp() >= VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_START &&
180                        config.getProp() <= VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_END) {
181                    // internal property requires write to get notification
182                    VehiclePropValue v = VehicleNetworkTestUtil.createDummyValue(config.getProp(),
183                            config.getValueType());
184                    mVehicleNetwork.setProperty(v);
185                }
186            }
187        }
188        // now confirm event
189        for (Integer prop : subscribedProperties) {
190            mListener.waitForEvent(prop, TIMEOUT_MS);
191            mVehicleNetwork.unsubscribe(prop);
192        }
193        // wait for already patched events to go away.
194        Thread.sleep(1000);
195        mListener.resetEventRecord();
196        Thread.sleep(2000);
197        assertEquals(0, mListener.getActiveEventsCount());
198    }
199
200    private class TestListener implements VehicleNetworkListener {
201        private final ArraySet<Integer> mEventRecord = new ArraySet<Integer>();
202
203        @Override
204        public void onVehicleNetworkEvents(VehiclePropValues values) {
205            for (VehiclePropValue value : values.getValuesList()) {
206                Log.i(TAG, "event " + VehicleNetworkProtoUtil.VehiclePropValueToString(value));
207                synchronized (this) {
208                    mEventRecord.add(value.getProp());
209                    notifyAll();
210                }
211            }
212        }
213
214        @Override
215        public void onHalError(int errorCode, int property, int operation) {
216            // TODO Auto-generated method stub
217        }
218
219        @Override
220        public void onHalRestart(boolean inMocking) {
221            // TODO Auto-generated method stub
222        }
223
224        private synchronized boolean waitForEvent(Integer prop, long timeoutMs)
225                throws InterruptedException {
226            long now = SystemClock.elapsedRealtime();
227            long end = now + timeoutMs;
228            long timeToWait = end - now;
229            while (timeToWait > 0 && !mEventRecord.contains(prop)) {
230                wait(timeToWait);
231                timeToWait = end - SystemClock.elapsedRealtime();
232            }
233            return mEventRecord.contains(prop);
234        }
235
236        private synchronized void resetEventRecord() {
237            mEventRecord.clear();
238        }
239
240        private synchronized int getActiveEventsCount() {
241            return mEventRecord.size();
242        }
243    }
244}
245