OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library fletchc.test.program_result; | |
6 | |
7 import 'dart:convert' show | |
8 JSON; | |
9 | |
10 import 'source_update.dart'; | |
11 | |
12 class ProgramResult { | |
13 final /* Map<String, String> or String */ code; | |
14 | |
15 final List<String> messages; | |
16 | |
17 final bool compileUpdatesShouldThrow; | |
18 | |
19 final bool commitChangesShouldFail; | |
20 | |
21 final bool hasCompileTimeError; | |
22 | |
23 const ProgramResult( | |
24 this.code, | |
25 this.messages, | |
26 {this.compileUpdatesShouldThrow: false, | |
27 this.commitChangesShouldFail: false, | |
28 this.hasCompileTimeError: false}); | |
29 | |
30 String toString() { | |
31 return """ | |
32 ProgramResult( | |
33 ${JSON.encode(code)}, | |
34 ${JSON.encode(messages)}, | |
35 commitChangesShouldFail: $commitChangesShouldFail, | |
36 compileUpdatesShouldThrow: $compileUpdatesShouldThrow, | |
37 hasCompileTimeError: $hasCompileTimeError)"""; | |
38 } | |
39 } | |
40 | |
41 class ProgramExpectation { | |
42 final List<String> messages; | |
43 | |
44 final bool compileUpdatesShouldThrow; | |
45 | |
46 final bool commitChangesShouldFail; | |
47 | |
48 final bool hasCompileTimeError; | |
49 | |
50 const ProgramExpectation( | |
51 this.messages, | |
52 {this.compileUpdatesShouldThrow: false, | |
53 this.commitChangesShouldFail: false, | |
54 this.hasCompileTimeError: false}); | |
55 | |
56 factory ProgramExpectation.fromJson(String json) { | |
57 var data = JSON.decode(json); | |
58 if (data is String) { | |
59 data = <String>[data]; | |
60 } | |
61 if (data is List) { | |
62 return new ProgramExpectation(data); | |
63 } | |
64 return new ProgramExpectation( | |
65 extractMessages(data), | |
66 compileUpdatesShouldThrow: extractCompileUpdatesShouldThrow(data), | |
67 commitChangesShouldFail: extractCommitChangesShouldFail(data), | |
68 hasCompileTimeError: extractHasCompileTimeError(data)); | |
69 } | |
70 | |
71 ProgramResult toResult(/* Map<String, String> or String */ code) { | |
72 return new ProgramResult( | |
73 code, | |
74 messages, | |
75 compileUpdatesShouldThrow: compileUpdatesShouldThrow, | |
76 commitChangesShouldFail: commitChangesShouldFail, | |
77 hasCompileTimeError: hasCompileTimeError); | |
78 } | |
79 | |
80 toJson() { | |
81 if (!compileUpdatesShouldThrow && !commitChangesShouldFail) { | |
82 return messages.length == 1 ? messages.first : messages; | |
83 } | |
84 Map<String, dynamic> result = <String, dynamic>{ | |
85 "messages": messages, | |
86 }; | |
87 if (compileUpdatesShouldThrow) { | |
88 result['compileUpdatesShouldThrow'] = 1; | |
89 } | |
90 if (commitChangesShouldFail) { | |
91 result['commitChangesShouldFail'] = 1; | |
92 } | |
93 if (hasCompileTimeError) { | |
94 result['hasCompileTimeError'] = 1; | |
95 } | |
96 return result; | |
97 } | |
98 | |
99 String toString() { | |
100 return """ | |
101 ProgramExpectation( | |
102 ${JSON.encode(messages)}, | |
103 commitChangesShouldFail: $commitChangesShouldFail, | |
104 compileUpdatesShouldThrow: $compileUpdatesShouldThrow, | |
105 hasCompileTimeError: $hasCompileTimeError)"""; | |
106 } | |
107 | |
108 static List<String> extractMessages(Map<String, dynamic> json) { | |
109 return new List<String>.from(json["messages"]); | |
110 } | |
111 | |
112 static bool extractCompileUpdatesShouldThrow(Map<String, dynamic> json) { | |
113 return json["compileUpdatesShouldThrow"] == 1; | |
114 } | |
115 | |
116 static bool extractCommitChangesShouldFail(Map<String, dynamic> json) { | |
117 return json["commitChangesShouldFail"] == 1; | |
118 } | |
119 | |
120 static bool extractHasCompileTimeError(Map<String, dynamic> json) { | |
121 return json["hasCompileTimeError"] == 1; | |
122 } | |
123 } | |
124 | |
125 class EncodedResult { | |
126 final /* String or List */ updates; | |
127 | |
128 final List expectations; | |
129 | |
130 const EncodedResult(this.updates, this.expectations); | |
131 | |
132 List<ProgramResult> decode() { | |
133 if (updates is List) { | |
134 if (updates.length == 1) { | |
135 throw new StateError("Trivial diff, no reason to use decode."); | |
136 } | |
137 List<String> sources = expandUpdates(updates); | |
138 List expectations = this.expectations; | |
139 if (sources.length != expectations.length) { | |
140 throw new StateError( | |
141 "Number of sources and expectations differ" | |
142 " (${sources.length} sources," | |
143 " ${expectations.length} expectations)."); | |
144 } | |
145 List<ProgramResult> result = new List<ProgramResult>(sources.length); | |
146 for (int i = 0; i < sources.length; i++) { | |
147 result[i] = expectations[i].toResult(sources[i]); | |
148 } | |
149 return result; | |
150 } else if (updates is String) { | |
151 Map<String, String> files = splitFiles(updates); | |
152 Map<String, List<String>> fileMap = <String, List<String>>{}; | |
153 int updateCount = -1; | |
154 for (String name in files.keys) { | |
155 if (name.endsWith(".patch")) { | |
156 String realname = name.substring(0, name.length - ".patch".length); | |
157 if (files.containsKey(realname)) { | |
158 throw new StateError("Patch '$name' conflicts with '$realname'"); | |
159 } | |
160 if (fileMap.containsKey(realname)) { | |
161 // Can't happen. | |
162 throw new StateError("Duplicated entry for '$realname'."); | |
163 } | |
164 List<String> updates = expandUpdates(expandDiff(files[name])); | |
165 if (updates.length == 1) { | |
166 throw new StateError("No patches found in:\n ${files[name]}"); | |
167 } | |
168 if (updateCount == -1) { | |
169 updateCount = updates.length; | |
170 } else if (updateCount != updates.length) { | |
171 throw new StateError( | |
172 "Unexpected number of patches: ${updates.length}," | |
173 " expected ${updateCount}"); | |
174 } | |
175 fileMap[realname] = updates; | |
176 } | |
177 } | |
178 if (updateCount == -1) { | |
179 throw new StateError("No patch files in $updates"); | |
180 } | |
181 for (String name in files.keys) { | |
182 if (!name.endsWith(".patch")) { | |
183 fileMap[name] = new List<String>.filled(updateCount, files[name]); | |
184 } | |
185 } | |
186 if (updateCount != expectations.length) { | |
187 throw new StateError( | |
188 "Number of patches and expectations differ " | |
189 "(${updateCount} patches, ${expectations.length} expectations)."); | |
190 } | |
191 List<ProgramResult> result = new List<ProgramResult>(updateCount); | |
192 for (int i = 0; i < updateCount; i++) { | |
193 ProgramExpectation expectation = decodeExpectation(expectations[i]); | |
194 result[i] = expectation.toResult(<String, String>{}); | |
195 } | |
196 for (String name in fileMap.keys) { | |
197 for (int i = 0; i < updateCount; i++) { | |
198 result[i].code[name] = fileMap[name][i]; | |
199 } | |
200 } | |
201 return result; | |
202 } else { | |
203 throw new StateError("Unknown encoding of updates"); | |
204 } | |
205 } | |
206 } | |
207 | |
208 ProgramExpectation decodeExpectation(expectation) { | |
209 if (expectation is ProgramExpectation) { | |
210 return expectation; | |
211 } else if (expectation is String) { | |
212 return new ProgramExpectation(<String>[expectation]); | |
213 } else if (expectation is List) { | |
214 return new ProgramExpectation(new List<String>.from(expectation)); | |
215 } else { | |
216 throw new ArgumentError("Don't know how to decode $expectation"); | |
217 } | |
218 } | |
219 | |
220 Map<String, EncodedResult> computeTests(List<String> tests) { | |
221 Map<String, EncodedResult> result = <String, EncodedResult>{}; | |
222 for (String test in tests) { | |
223 int firstLineEnd = test.indexOf("\n"); | |
224 String testName = test.substring(0, firstLineEnd); | |
225 test = test.substring(firstLineEnd + 1); | |
226 Map<String, String> files = splitFiles(test); | |
227 bool isFirstPatch = true; | |
228 List<ProgramExpectation> expectations; | |
229 files.forEach((String filename, String source) { | |
230 if (filename.endsWith(".patch")) { | |
231 if (isFirstPatch) { | |
232 expectations = extractJsonExpectations(source); | |
233 } | |
234 isFirstPatch = false; | |
235 } | |
236 }); | |
237 if (result.containsKey(testName)) { | |
238 throw new StateError("'$testName' is duplicated"); | |
239 } | |
240 result[testName] = new EncodedResult(test, expectations); | |
241 } | |
242 return result; | |
243 } | |
244 | |
245 List<ProgramExpectation> extractJsonExpectations(String source) { | |
246 return new List<ProgramExpectation>.from(source.split("\n") | |
247 .where((l) => l.startsWith("<<<< ") || l.startsWith("==== ")) | |
248 .map((l) => l.substring("<<<< ".length)) | |
249 .map((l) => new ProgramExpectation.fromJson(l))); | |
250 } | |
OLD | NEW |