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