• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package com.android.car;
18 
19 import android.support.test.filters.MediumTest;
20 import android.support.test.runner.AndroidJUnit4;
21 import android.util.Log;
22 
23 import junit.framework.TestCase;
24 
25 import org.junit.Test;
26 import org.junit.runner.RunWith;
27 
28 import java.lang.reflect.Field;
29 import java.lang.reflect.Modifier;
30 import java.util.Arrays;
31 import java.util.HashMap;
32 import java.util.Map;
33 
34 /**
35  * Validates that diagnostic constants in CarService and Vehicle HAL have the same value
36  * This is an important assumption to validate because we do not perform any mapping between
37  * the two layers, instead relying on the constants on both sides having identical values.
38  */
39 @RunWith(AndroidJUnit4.class)
40 @MediumTest
41 public class CarDiagnosticConstantsTest extends TestCase {
42     static final String TAG = CarDiagnosticConstantsTest.class.getSimpleName();
43 
44     static class MismatchException extends Exception {
dumpClass(Class<?> clazz)45         private static String dumpClass(Class<?> clazz) {
46             StringBuilder builder = new StringBuilder(clazz.getName() + "{\n");
47             Arrays.stream(clazz.getFields()).forEach((Field field) -> {
48                 builder.append('\t').append(field.toString()).append('\n');
49             });
50             return builder.append('}').toString();
51         }
52 
logClasses(Class<?> clazz1, Class<?> clazz2)53         private static void logClasses(Class<?> clazz1, Class<?> clazz2) {
54             Log.d(TAG, "MismatchException. class1: " + dumpClass(clazz1));
55             Log.d(TAG, "MismatchException. class2: " + dumpClass(clazz2));
56         }
57 
MismatchException(String message)58         MismatchException(String message) {
59             super(message);
60         }
61 
fieldValueMismatch(Class<?> clazz1, Class<?> clazz2, String name, int value1, int value2)62         static MismatchException fieldValueMismatch(Class<?> clazz1, Class<?> clazz2, String name,
63                 int value1, int value2) {
64             logClasses(clazz1, clazz2);
65             return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
66                 " field " + name  + " had different values " + value1 + " vs. " + value2);
67         }
68 
fieldsOnlyInClass1(Class<?> clazz1, Class<?> clazz2, Map<String, Integer> fields)69         static MismatchException fieldsOnlyInClass1(Class<?> clazz1, Class<?> clazz2,
70                 Map<String, Integer> fields) {
71             logClasses(clazz1, clazz2);
72             return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
73                 " some fields were only found in the first class:\n" +
74                 fields.keySet().stream().reduce("",
75                     (String s, String t) -> s + "\n" + t));
76         }
77 
fieldOnlyInClass2(Class<?> clazz1, Class<?> clazz2, String field)78         static MismatchException fieldOnlyInClass2(Class<?> clazz1, Class<?> clazz2, String field) {
79             logClasses(clazz1, clazz2);
80             return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
81                 " field " + field + " was not found in both classes");
82         }
83     }
84 
isPublicStaticFinalInt(Field field)85     static boolean isPublicStaticFinalInt(Field field) {
86         final int modifiers = field.getModifiers();
87         final boolean isPublic = (modifiers & Modifier.PUBLIC) == Modifier.PUBLIC;
88         final boolean isStatic = (modifiers & Modifier.STATIC) == Modifier.STATIC;
89         final boolean isFinal = (modifiers & Modifier.FINAL) == Modifier.FINAL;
90         if (isPublic && isStatic && isFinal) {
91             return field.getType() == int.class;
92         }
93         return false;
94     }
95 
validateMatch(Class<?> clazz1, Class<?> clazz2)96     static void validateMatch(Class<?> clazz1, Class<?> clazz2) throws Exception {
97         Map<String, Integer> fields = new HashMap<>();
98 
99         // add all the fields in the first class to a map
100         Arrays.stream(clazz1.getFields()).filter(
101             CarDiagnosticConstantsTest::isPublicStaticFinalInt).forEach( (Field field) -> {
102                 final String name = field.getName();
103                 try {
104                     fields.put(name, field.getInt(null));
105                 } catch (IllegalAccessException e) {
106                     // this will practically never happen because we checked that it is a
107                     // public static final field before reading from it
108                     Log.wtf(TAG, String.format("attempt to access field %s threw exception",
109                         field.toString()), e);
110                 }
111             });
112 
113         // check for all fields in the second class, and remove matches from the map
114         for (Field field2 : clazz2.getFields()) {
115             if (isPublicStaticFinalInt(field2)) {
116                 final String name = field2.getName();
117                 if (fields.containsKey(name)) {
118                     try {
119                         final int value2 = field2.getInt(null);
120                         final int value1 = fields.getOrDefault(name, value2+1);
121                         if (value2 != value1) {
122                             throw MismatchException.fieldValueMismatch(clazz1, clazz2,
123                                 field2.getName(), value1, value2);
124                         }
125                         fields.remove(name);
126                     } catch (IllegalAccessException e) {
127                         // this will practically never happen because we checked that it is a
128                         // public static final field before reading from it
129                         Log.wtf(TAG, String.format("attempt to access field %s threw exception",
130                             field2.toString()), e);
131                         throw e;
132                     }
133                 } else {
134                     throw MismatchException.fieldOnlyInClass2(clazz1, clazz2, name);
135                 }
136             }
137         }
138 
139         // if anything is left, we didn't find some fields in the second class
140         if (!fields.isEmpty()) {
141             throw MismatchException.fieldsOnlyInClass1(clazz1, clazz2, fields);
142         }
143     }
144 
145     @Test
testFuelSystemStatus()146     public void testFuelSystemStatus() throws Exception {
147         validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2FuelSystemStatus.class,
148             android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus.class);
149     }
150 
testFuelType()151     @Test public void testFuelType() throws Exception {
152         validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2FuelType.class,
153             android.car.diagnostic.CarDiagnosticEvent.FuelType.class);
154     }
155 
testSecondaryAirStatus()156     @Test public void testSecondaryAirStatus() throws Exception {
157         validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2SecondaryAirStatus.class,
158             android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus.class);
159     }
160 
testIgnitionMonitors()161     @Test public void testIgnitionMonitors() throws Exception {
162         validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2CommonIgnitionMonitors.class,
163             android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.class);
164 
165         validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2CompressionIgnitionMonitors.class,
166             android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.class);
167 
168         validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2SparkIgnitionMonitors.class,
169             android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.class);
170     }
171 }
172