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 // These tests fork a second VM process that runs the script | |
6 // ``tools/full-coverage.dart'' and verifies that the tool | |
7 // produces the expeced output. | |
8 | |
9 import 'dart:async'; | |
10 import 'dart:convert'; | |
11 import 'dart:io'; | |
12 | |
13 import 'package:path/path.dart' as path; | |
14 import 'package:unittest/unittest.dart'; | |
15 | |
16 final String coverageScript = | |
17 Platform.script.resolve('../../tools/full-coverage.dart').toFilePath(); | |
18 final String packageRoot = Platform.packageRoot; | |
19 final List dartBaseArgs = ['--package-root=${packageRoot}', '--checked',]; | |
20 final Stopwatch sw = new Stopwatch(); | |
21 | |
22 int elapsed() => sw.elapsedMilliseconds; | |
23 | |
24 // With line numbers starting at 0, the list of hits can be understood as | |
25 // follows: | |
26 // * -1: No coverage data on this line. | |
27 // * 0: No hits on this line. | |
28 // * 1: ``Some'' hits on this line. | |
29 final coverageTests = [ | |
30 { | |
31 'name': 'faculty', | |
32 'program': ''' | |
33 dummy () { | |
34 for (int i = 0; i < 100; i++) { | |
35 print(i); | |
36 } | |
37 } | |
38 | |
39 int fac(int n) { | |
40 int f = 1; | |
41 for (int i = 1; i <= n; i++) { | |
42 f *= i; | |
43 } | |
44 return f; | |
45 } | |
46 | |
47 main() { | |
48 if (false) { | |
49 dummy(11); | |
50 } else { | |
51 fac(10); | |
52 } | |
53 } | |
54 ''', | |
55 'expectedHits': [-1, 0, 0, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, | |
56 -1, 0, -1, 1, -1, -1] | |
57 },{ | |
58 'name': 'closures', | |
59 'program': ''' | |
60 main() { | |
61 foo(bar) { | |
62 bar(); | |
63 } | |
64 | |
65 foo(() { | |
66 print("in closure"); | |
67 }); | |
68 } | |
69 ''', | |
70 'expectedHits': [-1, -1, 1, -1, -1, 1, 1, -1, -1] | |
71 } | |
72 ]; | |
73 | |
74 | |
75 String prepareEnv() { | |
76 Directory testDir = Directory.systemTemp.createTempSync("coverage-"); | |
77 for (var coverageProg in coverageTests) { | |
78 var coverageProgDir = new Directory( | |
79 path.join(testDir.path, coverageProg["name"])) | |
80 ..createSync(); | |
81 var f = new File(path.join(coverageProgDir.path, | |
82 "${coverageProg['name']}.dart")); | |
83 f.writeAsStringSync(coverageProg["program"], mode: FileMode.WRITE); | |
84 } | |
85 return testDir.path; | |
86 } | |
87 | |
88 | |
89 destroyEnv(base) => new Directory(base).deleteSync(recursive: true); | |
90 | |
91 | |
92 generateCoverage(String workingDirectory) { | |
93 for (var coverageProg in coverageTests) { | |
94 print('[+${elapsed()}ms] Generating data for ${coverageProg["name"]}'); | |
95 var progPath = path.join(workingDirectory, coverageProg['name']); | |
96 var script = path.join(progPath, "${coverageProg['name']}.dart"); | |
97 var dartArgs = new List.from(dartBaseArgs) | |
98 ..addAll(['--coverage-dir=${progPath}', '${script}']); | |
99 var result = Process.runSync(Platform.executable, dartArgs); | |
100 if (result.exitCode != 0) { | |
101 print("[+${elapsed()}ms] Got exitCode: ${result.exitCode}."); | |
102 print("stderr:\n${result.stderr}\n"); | |
103 expect(result.exitCode, 0); | |
104 } | |
105 } | |
106 } | |
107 | |
108 | |
109 Future<Process> convertCoverage(String programDir, String format) { | |
110 var dartArgs = new List.from(dartBaseArgs) | |
111 ..addAll([ | |
112 coverageScript, | |
113 '--package-root=${packageRoot}', | |
114 '--in=${programDir}', | |
115 format | |
116 ]); | |
117 return Process.start(Platform.executable, dartArgs); | |
118 } | |
119 | |
120 | |
121 class PrettyPrintDescriptor { | |
122 var _programPath; | |
123 var _validFormat = new RegExp(r"^\s*\d*\|.*$", multiLine: true); | |
124 var _pattern = new RegExp(r"^\s*(\d+)\|", multiLine: true); | |
125 | |
126 PrettyPrintDescriptor(this._programPath); | |
127 | |
128 get sectionStart => _programPath; | |
129 get sectionEnd => '/'; | |
130 get coverageParameter => '--pretty-print'; | |
131 | |
132 hitData(line) { | |
133 expect(_validFormat.hasMatch(line), isTrue); | |
134 var match = _pattern.firstMatch(line); | |
135 var result = -1; | |
136 if (match != null) { | |
137 result = (int.parse(match.group(1)) != 0) ? 1 : 0; | |
138 } | |
139 return [result]; | |
140 } | |
141 } | |
142 | |
143 | |
144 class LcovDescriptor { | |
145 var _pattern = new RegExp(r"^DA:(\d+),(\d+)$", multiLine: true); | |
146 var _programPath; | |
147 var _line_nr = 0; | |
148 | |
149 LcovDescriptor(this._programPath); | |
150 | |
151 get sectionStart => 'SF:${_programPath}'; | |
152 get sectionEnd => 'end_of_record'; | |
153 get coverageParameter => '--lcov'; | |
154 | |
155 hitData(line) { | |
156 expect(_pattern.hasMatch(line), isTrue); | |
157 var match = _pattern.firstMatch(line); | |
158 // Lcov data starts at line 1, we start at 0. | |
159 var out_line = int.parse(match[1]) - 1; | |
160 var hitCount = int.parse(match[2]); | |
161 var result = []; | |
162 for ( ; _line_nr < out_line; _line_nr++) { | |
163 result.add(-1); | |
164 } | |
165 result.add((hitCount != 0) ? 1 : 0); | |
166 _line_nr++; | |
167 return result; | |
168 } | |
169 } | |
170 | |
171 | |
172 Stream filterHitData(input, descriptor) { | |
173 bool in_program_section = false; | |
174 return input.where((line) { | |
175 if (in_program_section) { | |
176 if (line.startsWith(descriptor.sectionEnd)) { | |
177 in_program_section = false; | |
178 return false; | |
179 } | |
180 return true; | |
181 } | |
182 if (line.startsWith(descriptor.sectionStart)) { | |
183 in_program_section = true; | |
184 } | |
185 return false; | |
186 }).map((line) { | |
187 return descriptor.hitData(line); | |
188 }); | |
189 } | |
190 | |
191 | |
192 testCoverage(String programDir, String programPath, descriptor, | |
193 List expectedHitMap) { | |
194 var p = convertCoverage(programDir, descriptor.coverageParameter); | |
195 expect(p.then((process) { | |
196 var hitStream = filterHitData( | |
197 process.stdout.transform(UTF8.decoder) | |
198 .transform(const LineSplitter()), | |
199 descriptor); | |
200 var hitMap = []; | |
201 var subscription = hitStream.listen((data) { | |
202 // Flatten results. | |
203 data.forEach((e) => hitMap.add(e)); | |
204 }); | |
205 expect(subscription.asFuture().then((_) { | |
206 hitMap.forEach((e) { | |
207 expect(e, expectedHitMap.removeAt(0)); | |
208 }); | |
209 // Make sure that there are only lines left that do not contain coverage | |
210 // data. | |
211 expectedHitMap.forEach((e) => expect(e, -1)); | |
212 }), completes); | |
213 }), completes); | |
214 } | |
215 | |
216 | |
217 main() { | |
218 String testingDirectory; | |
219 sw.start(); | |
220 | |
221 setUp(() { | |
222 testingDirectory = prepareEnv(); | |
223 }); | |
224 | |
225 tearDown(() => destroyEnv(testingDirectory)); | |
226 | |
227 test('CoverageTests', () { | |
228 print('[+${elapsed()}ms] Generating coverage data...'); | |
229 generateCoverage(testingDirectory); | |
230 print('[+${elapsed()}ms] Done Generating coverage data.'); | |
231 | |
232 print('[+${elapsed()}ms] Running tests...'); | |
233 coverageTests.forEach((cTest) { | |
234 String programDir = path.join(testingDirectory, cTest['name']); | |
235 String programPath = path.join(programDir, "${cTest['name']}.dart"); | |
236 print('[+${elapsed()}ms] Testing lcov for ${cTest["name"]}'); | |
237 testCoverage(programDir, programPath, | |
238 new LcovDescriptor(programPath), | |
239 new List.from(cTest['expectedHits'])); | |
240 print('[+${elapsed()}ms] Testing pretty print for ${cTest["name"]}'); | |
241 testCoverage(programDir, programPath, | |
242 new PrettyPrintDescriptor(programPath), | |
243 new List.from(cTest['expectedHits'])); | |
244 }); | |
245 print('[+${elapsed()}ms] Done.'); | |
246 }); | |
247 } | |
OLD | NEW |