17b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata/*
27b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * Copyright (C) 2017 The Android Open Source Project
37b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata *
47b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * Licensed under the Apache License, Version 2.0 (the "License");
57b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * you may not use this file except in compliance with the License.
67b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * You may obtain a copy of the License at
77b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata *
87b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata *      http://www.apache.org/licenses/LICENSE-2.0
97b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata *
107b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * Unless required by applicable law or agreed to in writing, software
117b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * distributed under the License is distributed on an "AS IS" BASIS,
127b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * See the License for the specific language governing permissions and
147b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * limitations under the License.
157b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata */
167b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
177b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granatapackage com.android.car.test;
187b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
197b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granataimport android.test.suitebuilder.annotation.MediumTest;
207b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granataimport android.util.Log;
217b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
227b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granataimport java.lang.reflect.Field;
237b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granataimport java.lang.reflect.Modifier;
247b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granataimport java.util.Arrays;
257b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granataimport java.util.HashMap;
267b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granataimport java.util.Map;
277b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
287b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granataimport junit.framework.TestCase;
297b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
307b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata/**
317b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * Validates that diagnostic constants in CarService and Vehicle HAL have the same value
327b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * This is an important assumption to validate because we do not perform any mapping between
337b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata * the two layers, instead relying on the constants on both sides having identical values.
347b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata */
357b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata@MediumTest
367b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granatapublic class CarDiagnosticConstantsTest extends TestCase {
377b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    static final String TAG = CarDiagnosticConstantsTest.class.getSimpleName();
387b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
397b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    static class MismatchException extends Exception {
4064fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata        private static String dumpClass(Class<?> clazz) {
4164fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata            StringBuilder builder = new StringBuilder(clazz.getName() + "{\n");
4264fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata            Arrays.stream(clazz.getFields()).forEach((Field field) -> {
4364fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata                builder.append('\t').append(field.toString()).append('\n');
4464fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata            });
4564fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata            return builder.append('}').toString();
4664fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata        }
4764fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata
4864fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata        private static void logClasses(Class<?> clazz1, Class<?> clazz2) {
4964fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata            Log.d(TAG, "MismatchException. class1: " + dumpClass(clazz1));
5064fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata            Log.d(TAG, "MismatchException. class2: " + dumpClass(clazz2));
5164fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata        }
5264fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata
537b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        MismatchException(String message) {
547b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            super(message);
557b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        }
567b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
577b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        static MismatchException fieldValueMismatch(Class<?> clazz1, Class<?> clazz2, String name,
587b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                int value1, int value2) {
5964fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata            logClasses(clazz1, clazz2);
607b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
617b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                " field " + name  + " had different values " + value1 + " vs. " + value2);
627b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        }
637b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
647b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        static MismatchException fieldsOnlyInClass1(Class<?> clazz1, Class<?> clazz2,
657b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                Map<String, Integer> fields) {
6664fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata            logClasses(clazz1, clazz2);
677b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
687b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                " some fields were only found in the first class:\n" +
697b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                fields.keySet().stream().reduce("",
707b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                    (String s, String t) -> s + "\n" + t));
717b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        }
727b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
737b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        static MismatchException fieldOnlyInClass2(Class<?> clazz1, Class<?> clazz2, String field) {
7464fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata            logClasses(clazz1, clazz2);
757b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
767b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                " field " + field + " was not found in both classes");
777b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        }
787b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    }
797b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
807b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    static boolean isPublicStaticFinalInt(Field field) {
817b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        final int modifiers = field.getModifiers();
827b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        final boolean isPublic = (modifiers & Modifier.PUBLIC) == Modifier.PUBLIC;
837b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        final boolean isStatic = (modifiers & Modifier.STATIC) == Modifier.STATIC;
847b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        final boolean isFinal = (modifiers & Modifier.FINAL) == Modifier.FINAL;
857b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        if (isPublic && isStatic && isFinal) {
867b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            return field.getType() == int.class;
877b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        }
887b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        return false;
897b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    }
907b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
917b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    static void validateMatch(Class<?> clazz1, Class<?> clazz2) throws Exception {
927b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        Map<String, Integer> fields = new HashMap<>();
937b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
947b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        // add all the fields in the first class to a map
9564fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata        Arrays.stream(clazz1.getFields()).filter(
967b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            CarDiagnosticConstantsTest::isPublicStaticFinalInt).forEach( (Field field) -> {
977b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                final String name = field.getName();
987b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                try {
997b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                    fields.put(name, field.getInt(null));
1007b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                } catch (IllegalAccessException e) {
1017b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                    // this will practically never happen because we checked that it is a
1027b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                    // public static final field before reading from it
1037b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                    Log.wtf(TAG, String.format("attempt to access field %s threw exception",
1047b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        field.toString()), e);
1057b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                }
1067b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            });
1077b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
1087b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        // check for all fields in the second class, and remove matches from the map
10964fb54b08e2172a89ca025e94de37b3f9e08359cEnrico Granata        for (Field field2 : clazz2.getFields()) {
1107b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            if (isPublicStaticFinalInt(field2)) {
1117b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                final String name = field2.getName();
1127b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                if (fields.containsKey(name)) {
1137b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                    try {
1147b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        final int value2 = field2.getInt(null);
1157b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        final int value1 = fields.getOrDefault(name, value2+1);
1167b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        if (value2 != value1) {
1177b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                            throw MismatchException.fieldValueMismatch(clazz1, clazz2,
1187b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                                field2.getName(), value1, value2);
1197b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        }
1207b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        fields.remove(name);
1217b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                    } catch (IllegalAccessException e) {
1227b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        // this will practically never happen because we checked that it is a
1237b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        // public static final field before reading from it
1247b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        Log.wtf(TAG, String.format("attempt to access field %s threw exception",
1257b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                            field2.toString()), e);
1267b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                        throw e;
1277b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                    }
1287b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                } else {
1297b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                    throw MismatchException.fieldOnlyInClass2(clazz1, clazz2, name);
1307b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata                }
1317b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            }
1327b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        }
1337b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
1347b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        // if anything is left, we didn't find some fields in the second class
1357b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        if (!fields.isEmpty()) {
1367b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            throw MismatchException.fieldsOnlyInClass1(clazz1, clazz2, fields);
1377b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata        }
1387b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    }
1397b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
1407b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    public void testFuelSystemStatus() throws Exception {
14199e1a7555a9521c33203b68c5ab5bd1c039ee712Pavel Maltsev        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2FuelSystemStatus.class,
1427b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus.class);
1437b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    }
1447b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
1457b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    public void testFuelType() throws Exception {
14699e1a7555a9521c33203b68c5ab5bd1c039ee712Pavel Maltsev        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2FuelType.class,
1477b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            android.car.diagnostic.CarDiagnosticEvent.FuelType.class);
1487b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    }
1497b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
1507b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    public void testSecondaryAirStatus() throws Exception {
15199e1a7555a9521c33203b68c5ab5bd1c039ee712Pavel Maltsev        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2SecondaryAirStatus.class,
1527b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus.class);
1537b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    }
1547b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
1557b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    public void testIgnitionMonitors() throws Exception {
15699e1a7555a9521c33203b68c5ab5bd1c039ee712Pavel Maltsev        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2CommonIgnitionMonitors.class,
1577b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.class);
1587b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
15999e1a7555a9521c33203b68c5ab5bd1c039ee712Pavel Maltsev        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2CompressionIgnitionMonitors.class,
1607b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.class);
1617b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata
16299e1a7555a9521c33203b68c5ab5bd1c039ee712Pavel Maltsev        validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2SparkIgnitionMonitors.class,
1637b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata            android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.class);
1647b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata    }
1657b13666563e9c8603afe917043b35ec2b29c8ee4Enrico Granata}
166