1*795d594fSAndroid Build Coastguard Worker /* 2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2022 The Android Open Source Project 3*795d594fSAndroid Build Coastguard Worker * 4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*795d594fSAndroid Build Coastguard Worker * 8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*795d594fSAndroid Build Coastguard Worker * 10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*795d594fSAndroid Build Coastguard Worker * limitations under the License. 15*795d594fSAndroid Build Coastguard Worker */ 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker import com.google.gson.stream.JsonReader; 18*795d594fSAndroid Build Coastguard Worker import java.io.FileNotFoundException; 19*795d594fSAndroid Build Coastguard Worker import java.io.FileReader; 20*795d594fSAndroid Build Coastguard Worker import java.io.IOException; 21*795d594fSAndroid Build Coastguard Worker import java.util.HashSet; 22*795d594fSAndroid Build Coastguard Worker import java.util.LinkedHashSet; 23*795d594fSAndroid Build Coastguard Worker import java.util.List; 24*795d594fSAndroid Build Coastguard Worker import java.util.Set; 25*795d594fSAndroid Build Coastguard Worker import java.util.regex.Pattern; 26*795d594fSAndroid Build Coastguard Worker import java.util.regex.PatternSyntaxException; 27*795d594fSAndroid Build Coastguard Worker 28*795d594fSAndroid Build Coastguard Worker /** 29*795d594fSAndroid Build Coastguard Worker * Pre upload hook that ensures art-buildbot expectation files (files under //art/tools ending with 30*795d594fSAndroid Build Coastguard Worker * "_failures.txt", e.g. //art/tools/libcore_failures.txt) are well-formed json files. 31*795d594fSAndroid Build Coastguard Worker * 32*795d594fSAndroid Build Coastguard Worker * It makes basic validation of the keys but does not cover all the cases. Parser structure is 33*795d594fSAndroid Build Coastguard Worker * based on external/vogar/src/vogar/ExpectationStore.java. 34*795d594fSAndroid Build Coastguard Worker * 35*795d594fSAndroid Build Coastguard Worker * Hook is set up in //art/PREUPLOAD.cfg See also //tools/repohooks/README.md 36*795d594fSAndroid Build Coastguard Worker */ 37*795d594fSAndroid Build Coastguard Worker class PresubmitJsonLinter { 38*795d594fSAndroid Build Coastguard Worker 39*795d594fSAndroid Build Coastguard Worker private static final int FLAGS = Pattern.MULTILINE | Pattern.DOTALL; 40*795d594fSAndroid Build Coastguard Worker private static final Set<String> RESULTS = new HashSet<>(); 41*795d594fSAndroid Build Coastguard Worker 42*795d594fSAndroid Build Coastguard Worker static { 43*795d594fSAndroid Build Coastguard Worker RESULTS.addAll(List.of( 44*795d594fSAndroid Build Coastguard Worker "UNSUPPORTED", 45*795d594fSAndroid Build Coastguard Worker "COMPILE_FAILED", 46*795d594fSAndroid Build Coastguard Worker "EXEC_FAILED", 47*795d594fSAndroid Build Coastguard Worker "EXEC_TIMEOUT", 48*795d594fSAndroid Build Coastguard Worker "ERROR", 49*795d594fSAndroid Build Coastguard Worker "SUCCESS" 50*795d594fSAndroid Build Coastguard Worker )); 51*795d594fSAndroid Build Coastguard Worker } 52*795d594fSAndroid Build Coastguard Worker main(String[] args)53*795d594fSAndroid Build Coastguard Worker public static void main(String[] args) { 54*795d594fSAndroid Build Coastguard Worker for (String arg : args) { 55*795d594fSAndroid Build Coastguard Worker info("Checking " + arg); 56*795d594fSAndroid Build Coastguard Worker checkExpectationFile(arg); 57*795d594fSAndroid Build Coastguard Worker } 58*795d594fSAndroid Build Coastguard Worker } 59*795d594fSAndroid Build Coastguard Worker info(String message)60*795d594fSAndroid Build Coastguard Worker private static void info(String message) { 61*795d594fSAndroid Build Coastguard Worker System.err.println(message); 62*795d594fSAndroid Build Coastguard Worker } 63*795d594fSAndroid Build Coastguard Worker error(String message)64*795d594fSAndroid Build Coastguard Worker private static void error(String message) { 65*795d594fSAndroid Build Coastguard Worker System.err.println(message); 66*795d594fSAndroid Build Coastguard Worker System.exit(1); 67*795d594fSAndroid Build Coastguard Worker } 68*795d594fSAndroid Build Coastguard Worker checkExpectationFile(String arg)69*795d594fSAndroid Build Coastguard Worker private static void checkExpectationFile(String arg) { 70*795d594fSAndroid Build Coastguard Worker JsonReader reader; 71*795d594fSAndroid Build Coastguard Worker try { 72*795d594fSAndroid Build Coastguard Worker reader = new JsonReader(new FileReader(arg)); 73*795d594fSAndroid Build Coastguard Worker } catch (FileNotFoundException e) { 74*795d594fSAndroid Build Coastguard Worker error("File '" + arg + "' is not found"); 75*795d594fSAndroid Build Coastguard Worker return; 76*795d594fSAndroid Build Coastguard Worker } 77*795d594fSAndroid Build Coastguard Worker reader.setLenient(true); 78*795d594fSAndroid Build Coastguard Worker try { 79*795d594fSAndroid Build Coastguard Worker reader.beginArray(); 80*795d594fSAndroid Build Coastguard Worker while (reader.hasNext()) { 81*795d594fSAndroid Build Coastguard Worker readExpectation(reader); 82*795d594fSAndroid Build Coastguard Worker } 83*795d594fSAndroid Build Coastguard Worker reader.endArray(); 84*795d594fSAndroid Build Coastguard Worker } catch (IOException e) { 85*795d594fSAndroid Build Coastguard Worker error("Malformed json: " + reader); 86*795d594fSAndroid Build Coastguard Worker } 87*795d594fSAndroid Build Coastguard Worker } 88*795d594fSAndroid Build Coastguard Worker readExpectation(JsonReader reader)89*795d594fSAndroid Build Coastguard Worker private static void readExpectation(JsonReader reader) throws IOException { 90*795d594fSAndroid Build Coastguard Worker Set<String> names = new LinkedHashSet<String>(); 91*795d594fSAndroid Build Coastguard Worker Set<String> tags = new LinkedHashSet<String>(); 92*795d594fSAndroid Build Coastguard Worker boolean readResult = false; 93*795d594fSAndroid Build Coastguard Worker boolean readDescription = false; 94*795d594fSAndroid Build Coastguard Worker 95*795d594fSAndroid Build Coastguard Worker reader.beginObject(); 96*795d594fSAndroid Build Coastguard Worker while (reader.hasNext()) { 97*795d594fSAndroid Build Coastguard Worker String name = reader.nextName(); 98*795d594fSAndroid Build Coastguard Worker switch (name) { 99*795d594fSAndroid Build Coastguard Worker case "result": 100*795d594fSAndroid Build Coastguard Worker String result = reader.nextString(); 101*795d594fSAndroid Build Coastguard Worker if (!RESULTS.contains(result)) { 102*795d594fSAndroid Build Coastguard Worker error("Invalid 'result' value: '" + result + 103*795d594fSAndroid Build Coastguard Worker "'. Expected one of " + String.join(", ", RESULTS) + 104*795d594fSAndroid Build Coastguard Worker ". " + reader); 105*795d594fSAndroid Build Coastguard Worker } 106*795d594fSAndroid Build Coastguard Worker readResult = true; 107*795d594fSAndroid Build Coastguard Worker break; 108*795d594fSAndroid Build Coastguard Worker case "substring": { 109*795d594fSAndroid Build Coastguard Worker try { 110*795d594fSAndroid Build Coastguard Worker Pattern.compile( 111*795d594fSAndroid Build Coastguard Worker ".*" + Pattern.quote(reader.nextString()) + ".*", FLAGS); 112*795d594fSAndroid Build Coastguard Worker } catch (PatternSyntaxException e) { 113*795d594fSAndroid Build Coastguard Worker error("Malformed 'substring' value: " + reader); 114*795d594fSAndroid Build Coastguard Worker } 115*795d594fSAndroid Build Coastguard Worker } 116*795d594fSAndroid Build Coastguard Worker case "pattern": { 117*795d594fSAndroid Build Coastguard Worker try { 118*795d594fSAndroid Build Coastguard Worker Pattern.compile(reader.nextString(), FLAGS); 119*795d594fSAndroid Build Coastguard Worker } catch (PatternSyntaxException e) { 120*795d594fSAndroid Build Coastguard Worker error("Malformed 'pattern' value: " + reader); 121*795d594fSAndroid Build Coastguard Worker } 122*795d594fSAndroid Build Coastguard Worker break; 123*795d594fSAndroid Build Coastguard Worker } 124*795d594fSAndroid Build Coastguard Worker case "failure": 125*795d594fSAndroid Build Coastguard Worker names.add(reader.nextString()); 126*795d594fSAndroid Build Coastguard Worker break; 127*795d594fSAndroid Build Coastguard Worker case "description": 128*795d594fSAndroid Build Coastguard Worker reader.nextString(); 129*795d594fSAndroid Build Coastguard Worker readDescription = true; 130*795d594fSAndroid Build Coastguard Worker break; 131*795d594fSAndroid Build Coastguard Worker case "name": 132*795d594fSAndroid Build Coastguard Worker names.add(reader.nextString()); 133*795d594fSAndroid Build Coastguard Worker break; 134*795d594fSAndroid Build Coastguard Worker case "names": 135*795d594fSAndroid Build Coastguard Worker readStrings(reader, names); 136*795d594fSAndroid Build Coastguard Worker break; 137*795d594fSAndroid Build Coastguard Worker case "tags": 138*795d594fSAndroid Build Coastguard Worker readStrings(reader, tags); 139*795d594fSAndroid Build Coastguard Worker break; 140*795d594fSAndroid Build Coastguard Worker case "bug": 141*795d594fSAndroid Build Coastguard Worker reader.nextLong(); 142*795d594fSAndroid Build Coastguard Worker break; 143*795d594fSAndroid Build Coastguard Worker case "modes": 144*795d594fSAndroid Build Coastguard Worker readModes(reader); 145*795d594fSAndroid Build Coastguard Worker break; 146*795d594fSAndroid Build Coastguard Worker case "modes_variants": 147*795d594fSAndroid Build Coastguard Worker readModesAndVariants(reader); 148*795d594fSAndroid Build Coastguard Worker break; 149*795d594fSAndroid Build Coastguard Worker default: 150*795d594fSAndroid Build Coastguard Worker error("Unknown key '" + name + "' in expectations file"); 151*795d594fSAndroid Build Coastguard Worker reader.skipValue(); 152*795d594fSAndroid Build Coastguard Worker break; 153*795d594fSAndroid Build Coastguard Worker } 154*795d594fSAndroid Build Coastguard Worker } 155*795d594fSAndroid Build Coastguard Worker reader.endObject(); 156*795d594fSAndroid Build Coastguard Worker 157*795d594fSAndroid Build Coastguard Worker if (names.isEmpty()) { 158*795d594fSAndroid Build Coastguard Worker error("Missing 'name' or 'failure' key in " + reader); 159*795d594fSAndroid Build Coastguard Worker } 160*795d594fSAndroid Build Coastguard Worker if (!readResult) { 161*795d594fSAndroid Build Coastguard Worker error("Missing 'result' key in " + reader); 162*795d594fSAndroid Build Coastguard Worker } 163*795d594fSAndroid Build Coastguard Worker if (!readDescription) { 164*795d594fSAndroid Build Coastguard Worker error("Missing 'description' key in " + reader); 165*795d594fSAndroid Build Coastguard Worker } 166*795d594fSAndroid Build Coastguard Worker } 167*795d594fSAndroid Build Coastguard Worker readStrings(JsonReader reader, Set<String> output)168*795d594fSAndroid Build Coastguard Worker private static void readStrings(JsonReader reader, Set<String> output) throws IOException { 169*795d594fSAndroid Build Coastguard Worker reader.beginArray(); 170*795d594fSAndroid Build Coastguard Worker while (reader.hasNext()) { 171*795d594fSAndroid Build Coastguard Worker output.add(reader.nextString()); 172*795d594fSAndroid Build Coastguard Worker } 173*795d594fSAndroid Build Coastguard Worker reader.endArray(); 174*795d594fSAndroid Build Coastguard Worker } 175*795d594fSAndroid Build Coastguard Worker readModes(JsonReader reader)176*795d594fSAndroid Build Coastguard Worker private static void readModes(JsonReader reader) throws IOException { 177*795d594fSAndroid Build Coastguard Worker reader.beginArray(); 178*795d594fSAndroid Build Coastguard Worker while (reader.hasNext()) { 179*795d594fSAndroid Build Coastguard Worker reader.nextString(); 180*795d594fSAndroid Build Coastguard Worker } 181*795d594fSAndroid Build Coastguard Worker reader.endArray(); 182*795d594fSAndroid Build Coastguard Worker } 183*795d594fSAndroid Build Coastguard Worker 184*795d594fSAndroid Build Coastguard Worker /** 185*795d594fSAndroid Build Coastguard Worker * Expected format: mode_variants: [["host", "X32"], ["host", "X64"]] 186*795d594fSAndroid Build Coastguard Worker */ readModesAndVariants(JsonReader reader)187*795d594fSAndroid Build Coastguard Worker private static void readModesAndVariants(JsonReader reader) throws IOException { 188*795d594fSAndroid Build Coastguard Worker reader.beginArray(); 189*795d594fSAndroid Build Coastguard Worker while (reader.hasNext()) { 190*795d594fSAndroid Build Coastguard Worker reader.beginArray(); 191*795d594fSAndroid Build Coastguard Worker reader.nextString(); 192*795d594fSAndroid Build Coastguard Worker reader.nextString(); 193*795d594fSAndroid Build Coastguard Worker reader.endArray(); 194*795d594fSAndroid Build Coastguard Worker } 195*795d594fSAndroid Build Coastguard Worker reader.endArray(); 196*795d594fSAndroid Build Coastguard Worker } 197*795d594fSAndroid Build Coastguard Worker }