xref: /aosp_15_r20/art/tools/PresubmitJsonLinter.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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 }