1 | //===- unittest/ProfileData/CoverageMappingTest.cpp -------------------------=// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/ProfileData/Coverage/CoverageMapping.h" |
10 | #include "llvm/ProfileData/Coverage/CoverageMappingReader.h" |
11 | #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" |
12 | #include "llvm/ProfileData/InstrProfReader.h" |
13 | #include "llvm/ProfileData/InstrProfWriter.h" |
14 | #include "llvm/Support/raw_ostream.h" |
15 | #include "llvm/Testing/Support/Error.h" |
16 | #include "llvm/Testing/Support/SupportHelpers.h" |
17 | #include "gtest/gtest.h" |
18 | |
19 | #include <map> |
20 | #include <ostream> |
21 | #include <utility> |
22 | |
23 | using namespace llvm; |
24 | using namespace coverage; |
25 | |
26 | [[nodiscard]] static ::testing::AssertionResult |
27 | ErrorEquals(Error E, coveragemap_error Expected_Err, |
28 | const std::string &Expected_Msg = std::string()) { |
29 | coveragemap_error Found; |
30 | std::string Msg; |
31 | std::string FoundMsg; |
32 | handleAllErrors(E: std::move(E), Handlers: [&](const CoverageMapError &CME) { |
33 | Found = CME.get(); |
34 | Msg = CME.getMessage(); |
35 | FoundMsg = CME.message(); |
36 | }); |
37 | if (Expected_Err == Found && Msg == Expected_Msg) |
38 | return ::testing::AssertionSuccess(); |
39 | return ::testing::AssertionFailure() << "error: " << FoundMsg << "\n" ; |
40 | } |
41 | |
42 | namespace llvm { |
43 | namespace coverage { |
44 | void PrintTo(const Counter &C, ::std::ostream *os) { |
45 | if (C.isZero()) |
46 | *os << "Zero" ; |
47 | else if (C.isExpression()) |
48 | *os << "Expression " << C.getExpressionID(); |
49 | else |
50 | *os << "Counter " << C.getCounterID(); |
51 | } |
52 | |
53 | void PrintTo(const CoverageSegment &S, ::std::ostream *os) { |
54 | *os << "CoverageSegment(" << S.Line << ", " << S.Col << ", " ; |
55 | if (S.HasCount) |
56 | *os << S.Count << ", " ; |
57 | *os << (S.IsRegionEntry ? "true" : "false" ) << ")" ; |
58 | } |
59 | } |
60 | } |
61 | |
62 | namespace { |
63 | |
64 | struct OutputFunctionCoverageData { |
65 | StringRef Name; |
66 | uint64_t Hash; |
67 | std::vector<StringRef> Filenames; |
68 | std::vector<CounterMappingRegion> Regions; |
69 | std::vector<CounterExpression> Expressions; |
70 | |
71 | OutputFunctionCoverageData() : Hash(0) {} |
72 | |
73 | OutputFunctionCoverageData(OutputFunctionCoverageData &&OFCD) |
74 | : Name(OFCD.Name), Hash(OFCD.Hash), Filenames(std::move(OFCD.Filenames)), |
75 | Regions(std::move(OFCD.Regions)) {} |
76 | |
77 | OutputFunctionCoverageData(const OutputFunctionCoverageData &) = delete; |
78 | OutputFunctionCoverageData & |
79 | operator=(const OutputFunctionCoverageData &) = delete; |
80 | OutputFunctionCoverageData &operator=(OutputFunctionCoverageData &&) = delete; |
81 | |
82 | void fillCoverageMappingRecord(CoverageMappingRecord &Record) const { |
83 | Record.FunctionName = Name; |
84 | Record.FunctionHash = Hash; |
85 | Record.Filenames = Filenames; |
86 | Record.Expressions = Expressions; |
87 | Record.MappingRegions = Regions; |
88 | } |
89 | }; |
90 | |
91 | struct CoverageMappingReaderMock : CoverageMappingReader { |
92 | ArrayRef<OutputFunctionCoverageData> Functions; |
93 | |
94 | CoverageMappingReaderMock(ArrayRef<OutputFunctionCoverageData> Functions) |
95 | : Functions(Functions) {} |
96 | |
97 | Error readNextRecord(CoverageMappingRecord &Record) override { |
98 | if (Functions.empty()) |
99 | return make_error<CoverageMapError>(Args: coveragemap_error::eof); |
100 | |
101 | Functions.front().fillCoverageMappingRecord(Record); |
102 | Functions = Functions.slice(N: 1); |
103 | |
104 | return Error::success(); |
105 | } |
106 | }; |
107 | |
108 | struct InputFunctionCoverageData { |
109 | // Maps the global file index from CoverageMappingTest.Files |
110 | // to the index of that file within this function. We can't just use |
111 | // global file indexes here because local indexes have to be dense. |
112 | // This map is used during serialization to create the virtual file mapping |
113 | // (from local fileId to global Index) in the head of the per-function |
114 | // coverage mapping data. |
115 | SmallDenseMap<unsigned, unsigned> ReverseVirtualFileMapping; |
116 | std::string Name; |
117 | uint64_t Hash; |
118 | std::vector<CounterMappingRegion> Regions; |
119 | std::vector<CounterExpression> Expressions; |
120 | |
121 | InputFunctionCoverageData(std::string Name, uint64_t Hash) |
122 | : Name(std::move(Name)), Hash(Hash) {} |
123 | |
124 | InputFunctionCoverageData(InputFunctionCoverageData &&IFCD) |
125 | : ReverseVirtualFileMapping(std::move(IFCD.ReverseVirtualFileMapping)), |
126 | Name(std::move(IFCD.Name)), Hash(IFCD.Hash), |
127 | Regions(std::move(IFCD.Regions)) {} |
128 | |
129 | InputFunctionCoverageData(const InputFunctionCoverageData &) = delete; |
130 | InputFunctionCoverageData & |
131 | operator=(const InputFunctionCoverageData &) = delete; |
132 | InputFunctionCoverageData &operator=(InputFunctionCoverageData &&) = delete; |
133 | }; |
134 | |
135 | struct CoverageMappingTest : ::testing::TestWithParam<std::tuple<bool, bool>> { |
136 | bool UseMultipleReaders; |
137 | StringMap<unsigned> Files; |
138 | std::vector<std::string> Filenames; |
139 | std::vector<InputFunctionCoverageData> InputFunctions; |
140 | std::vector<OutputFunctionCoverageData> OutputFunctions; |
141 | |
142 | InstrProfWriter ProfileWriter; |
143 | std::unique_ptr<IndexedInstrProfReader> ProfileReader; |
144 | |
145 | std::unique_ptr<CoverageMapping> LoadedCoverage; |
146 | |
147 | void SetUp() override { |
148 | ProfileWriter.setOutputSparse(std::get<0>(t: GetParam())); |
149 | UseMultipleReaders = std::get<1>(t: GetParam()); |
150 | } |
151 | |
152 | unsigned getGlobalFileIndex(StringRef Name) { |
153 | auto R = Files.find(Key: Name); |
154 | if (R != Files.end()) |
155 | return R->second; |
156 | unsigned Index = Files.size() + 1; |
157 | Files.try_emplace(Key: Name, Args&: Index); |
158 | return Index; |
159 | } |
160 | |
161 | // Return the file index of file 'Name' for the current function. |
162 | // Add the file into the global map if necessary. |
163 | // See also InputFunctionCoverageData::ReverseVirtualFileMapping |
164 | // for additional comments. |
165 | unsigned getFileIndexForFunction(StringRef Name) { |
166 | unsigned GlobalIndex = getGlobalFileIndex(Name); |
167 | auto &CurrentFunctionFileMapping = |
168 | InputFunctions.back().ReverseVirtualFileMapping; |
169 | auto R = CurrentFunctionFileMapping.find(Val: GlobalIndex); |
170 | if (R != CurrentFunctionFileMapping.end()) |
171 | return R->second; |
172 | unsigned IndexInFunction = CurrentFunctionFileMapping.size(); |
173 | CurrentFunctionFileMapping.insert( |
174 | KV: std::make_pair(x&: GlobalIndex, y&: IndexInFunction)); |
175 | return IndexInFunction; |
176 | } |
177 | |
178 | void startFunction(StringRef FuncName, uint64_t Hash) { |
179 | InputFunctions.emplace_back(args: FuncName.str(), args&: Hash); |
180 | } |
181 | |
182 | void addCMR(Counter C, StringRef File, unsigned LS, unsigned CS, unsigned LE, |
183 | unsigned CE, bool Skipped = false) { |
184 | auto &Regions = InputFunctions.back().Regions; |
185 | unsigned FileID = getFileIndexForFunction(Name: File); |
186 | Regions.push_back( |
187 | x: Skipped ? CounterMappingRegion::makeSkipped(FileID, LineStart: LS, ColumnStart: CS, LineEnd: LE, ColumnEnd: CE) |
188 | : CounterMappingRegion::makeRegion(Count: C, FileID, LineStart: LS, ColumnStart: CS, LineEnd: LE, ColumnEnd: CE)); |
189 | } |
190 | |
191 | void addSkipped(StringRef File, unsigned LS, unsigned CS, unsigned LE, |
192 | unsigned CE) { |
193 | addCMR(C: Counter::getZero(), File, LS, CS, LE, CE, Skipped: true); |
194 | } |
195 | |
196 | void addMCDCDecisionCMR(unsigned Mask, uint16_t NC, StringRef File, |
197 | unsigned LS, unsigned CS, unsigned LE, unsigned CE) { |
198 | auto &Regions = InputFunctions.back().Regions; |
199 | unsigned FileID = getFileIndexForFunction(Name: File); |
200 | Regions.push_back(x: CounterMappingRegion::makeDecisionRegion( |
201 | MCDCParams: mcdc::DecisionParameters{Mask, NC}, FileID, LineStart: LS, ColumnStart: CS, LineEnd: LE, ColumnEnd: CE)); |
202 | } |
203 | |
204 | void addMCDCBranchCMR(Counter C1, Counter C2, mcdc::ConditionID ID, |
205 | mcdc::ConditionIDs Conds, StringRef File, unsigned LS, |
206 | unsigned CS, unsigned LE, unsigned CE) { |
207 | auto &Regions = InputFunctions.back().Regions; |
208 | unsigned FileID = getFileIndexForFunction(Name: File); |
209 | Regions.push_back(x: CounterMappingRegion::makeBranchRegion( |
210 | Count: C1, FalseCount: C2, FileID, LineStart: LS, ColumnStart: CS, LineEnd: LE, ColumnEnd: CE, MCDCParams: mcdc::BranchParameters{ID, Conds})); |
211 | } |
212 | |
213 | void addExpansionCMR(StringRef File, StringRef ExpandedFile, unsigned LS, |
214 | unsigned CS, unsigned LE, unsigned CE) { |
215 | InputFunctions.back().Regions.push_back(x: CounterMappingRegion::makeExpansion( |
216 | FileID: getFileIndexForFunction(Name: File), ExpandedFileID: getFileIndexForFunction(Name: ExpandedFile), |
217 | LineStart: LS, ColumnStart: CS, LineEnd: LE, ColumnEnd: CE)); |
218 | } |
219 | |
220 | void addExpression(CounterExpression CE) { |
221 | InputFunctions.back().Expressions.push_back(x: CE); |
222 | } |
223 | |
224 | std::string writeCoverageRegions(InputFunctionCoverageData &Data) { |
225 | SmallVector<unsigned, 8> FileIDs(Data.ReverseVirtualFileMapping.size()); |
226 | for (const auto &E : Data.ReverseVirtualFileMapping) |
227 | FileIDs[E.second] = E.first; |
228 | std::string Coverage; |
229 | llvm::raw_string_ostream OS(Coverage); |
230 | CoverageMappingWriter(FileIDs, Data.Expressions, Data.Regions).write(OS); |
231 | return OS.str(); |
232 | } |
233 | |
234 | void readCoverageRegions(const std::string &Coverage, |
235 | OutputFunctionCoverageData &Data) { |
236 | // We will re-use the StringRef in duplicate tests, clear it to avoid |
237 | // clobber previous ones. |
238 | Filenames.clear(); |
239 | Filenames.resize(new_size: Files.size() + 1); |
240 | for (const auto &E : Files) |
241 | Filenames[E.getValue()] = E.getKey().str(); |
242 | ArrayRef<std::string> FilenameRefs = llvm::ArrayRef(Filenames); |
243 | RawCoverageMappingReader Reader(Coverage, FilenameRefs, Data.Filenames, |
244 | Data.Expressions, Data.Regions); |
245 | EXPECT_THAT_ERROR(Reader.read(), Succeeded()); |
246 | } |
247 | |
248 | void writeAndReadCoverageRegions(bool EmitFilenames = true) { |
249 | OutputFunctions.resize(new_size: InputFunctions.size()); |
250 | for (unsigned I = 0; I < InputFunctions.size(); ++I) { |
251 | std::string Regions = writeCoverageRegions(Data&: InputFunctions[I]); |
252 | readCoverageRegions(Coverage: Regions, Data&: OutputFunctions[I]); |
253 | OutputFunctions[I].Name = InputFunctions[I].Name; |
254 | OutputFunctions[I].Hash = InputFunctions[I].Hash; |
255 | if (!EmitFilenames) |
256 | OutputFunctions[I].Filenames.clear(); |
257 | } |
258 | } |
259 | |
260 | void readProfCounts() { |
261 | auto Profile = ProfileWriter.writeBuffer(); |
262 | auto ReaderOrErr = IndexedInstrProfReader::create(Buffer: std::move(Profile)); |
263 | EXPECT_THAT_ERROR(ReaderOrErr.takeError(), Succeeded()); |
264 | ProfileReader = std::move(ReaderOrErr.get()); |
265 | } |
266 | |
267 | Expected<std::unique_ptr<CoverageMapping>> readOutputFunctions() { |
268 | std::vector<std::unique_ptr<CoverageMappingReader>> CoverageReaders; |
269 | if (UseMultipleReaders) { |
270 | for (const auto &OF : OutputFunctions) { |
271 | ArrayRef<OutputFunctionCoverageData> Funcs(OF); |
272 | CoverageReaders.push_back( |
273 | x: std::make_unique<CoverageMappingReaderMock>(args&: Funcs)); |
274 | } |
275 | } else { |
276 | ArrayRef<OutputFunctionCoverageData> Funcs(OutputFunctions); |
277 | CoverageReaders.push_back( |
278 | x: std::make_unique<CoverageMappingReaderMock>(args&: Funcs)); |
279 | } |
280 | return CoverageMapping::load(CoverageReaders, ProfileReader&: *ProfileReader); |
281 | } |
282 | |
283 | Error loadCoverageMapping(bool EmitFilenames = true) { |
284 | readProfCounts(); |
285 | writeAndReadCoverageRegions(EmitFilenames); |
286 | auto CoverageOrErr = readOutputFunctions(); |
287 | if (!CoverageOrErr) |
288 | return CoverageOrErr.takeError(); |
289 | LoadedCoverage = std::move(CoverageOrErr.get()); |
290 | return Error::success(); |
291 | } |
292 | }; |
293 | |
294 | TEST_P(CoverageMappingTest, basic_write_read) { |
295 | startFunction(FuncName: "func" , Hash: 0x1234); |
296 | addCMR(C: Counter::getCounter(CounterId: 0), File: "foo" , LS: 1, CS: 1, LE: 1, CE: 1); |
297 | addCMR(C: Counter::getCounter(CounterId: 1), File: "foo" , LS: 2, CS: 1, LE: 2, CE: 2); |
298 | addCMR(C: Counter::getZero(), File: "foo" , LS: 3, CS: 1, LE: 3, CE: 4); |
299 | addCMR(C: Counter::getCounter(CounterId: 2), File: "foo" , LS: 4, CS: 1, LE: 4, CE: 8); |
300 | addCMR(C: Counter::getCounter(CounterId: 3), File: "bar" , LS: 1, CS: 2, LE: 3, CE: 4); |
301 | |
302 | writeAndReadCoverageRegions(); |
303 | ASSERT_EQ(1u, InputFunctions.size()); |
304 | ASSERT_EQ(1u, OutputFunctions.size()); |
305 | InputFunctionCoverageData &Input = InputFunctions.back(); |
306 | OutputFunctionCoverageData &Output = OutputFunctions.back(); |
307 | |
308 | size_t N = ArrayRef(Input.Regions).size(); |
309 | ASSERT_EQ(N, Output.Regions.size()); |
310 | for (size_t I = 0; I < N; ++I) { |
311 | ASSERT_EQ(Input.Regions[I].Count, Output.Regions[I].Count); |
312 | ASSERT_EQ(Input.Regions[I].FileID, Output.Regions[I].FileID); |
313 | ASSERT_EQ(Input.Regions[I].startLoc(), Output.Regions[I].startLoc()); |
314 | ASSERT_EQ(Input.Regions[I].endLoc(), Output.Regions[I].endLoc()); |
315 | ASSERT_EQ(Input.Regions[I].Kind, Output.Regions[I].Kind); |
316 | } |
317 | } |
318 | |
319 | TEST_P(CoverageMappingTest, correct_deserialize_for_more_than_two_files) { |
320 | const char *FileNames[] = {"bar" , "baz" , "foo" }; |
321 | static const unsigned N = std::size(FileNames); |
322 | |
323 | startFunction(FuncName: "func" , Hash: 0x1234); |
324 | for (unsigned I = 0; I < N; ++I) |
325 | // Use LineStart to hold the index of the file name |
326 | // in order to preserve that information during possible sorting of CMRs. |
327 | addCMR(C: Counter::getCounter(CounterId: 0), File: FileNames[I], LS: I, CS: 1, LE: I, CE: 1); |
328 | |
329 | writeAndReadCoverageRegions(); |
330 | ASSERT_EQ(1u, OutputFunctions.size()); |
331 | OutputFunctionCoverageData &Output = OutputFunctions.back(); |
332 | |
333 | ASSERT_EQ(N, Output.Regions.size()); |
334 | ASSERT_EQ(N, Output.Filenames.size()); |
335 | |
336 | for (unsigned I = 0; I < N; ++I) { |
337 | ASSERT_GT(N, Output.Regions[I].FileID); |
338 | ASSERT_GT(N, Output.Regions[I].LineStart); |
339 | EXPECT_EQ(FileNames[Output.Regions[I].LineStart], |
340 | Output.Filenames[Output.Regions[I].FileID]); |
341 | } |
342 | } |
343 | |
344 | static const auto Err = [](Error E) { FAIL(); }; |
345 | |
346 | TEST_P(CoverageMappingTest, load_coverage_for_more_than_two_files) { |
347 | ProfileWriter.addRecord(I: {"func" , 0x1234, {0}}, Warn: Err); |
348 | |
349 | const char *FileNames[] = {"bar" , "baz" , "foo" }; |
350 | static const unsigned N = std::size(FileNames); |
351 | |
352 | startFunction(FuncName: "func" , Hash: 0x1234); |
353 | for (unsigned I = 0; I < N; ++I) |
354 | // Use LineStart to hold the index of the file name |
355 | // in order to preserve that information during possible sorting of CMRs. |
356 | addCMR(C: Counter::getCounter(CounterId: 0), File: FileNames[I], LS: I, CS: 1, LE: I, CE: 1); |
357 | |
358 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
359 | |
360 | for (unsigned I = 0; I < N; ++I) { |
361 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: FileNames[I]); |
362 | ASSERT_TRUE(!Data.empty()); |
363 | EXPECT_EQ(I, Data.begin()->Line); |
364 | } |
365 | } |
366 | |
367 | TEST_P(CoverageMappingTest, load_coverage_with_bogus_function_name) { |
368 | ProfileWriter.addRecord(I: {"" , 0x1234, {10}}, Warn: Err); |
369 | startFunction(FuncName: "" , Hash: 0x1234); |
370 | addCMR(C: Counter::getCounter(CounterId: 0), File: "foo" , LS: 1, CS: 1, LE: 5, CE: 5); |
371 | EXPECT_TRUE(ErrorEquals(loadCoverageMapping(), coveragemap_error::malformed, |
372 | "record function name is empty" )); |
373 | } |
374 | |
375 | TEST_P(CoverageMappingTest, load_coverage_for_several_functions) { |
376 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {10}}, Warn: Err); |
377 | ProfileWriter.addRecord(I: {"func2" , 0x2345, {20}}, Warn: Err); |
378 | |
379 | startFunction(FuncName: "func1" , Hash: 0x1234); |
380 | addCMR(C: Counter::getCounter(CounterId: 0), File: "foo" , LS: 1, CS: 1, LE: 5, CE: 5); |
381 | |
382 | startFunction(FuncName: "func2" , Hash: 0x2345); |
383 | addCMR(C: Counter::getCounter(CounterId: 0), File: "bar" , LS: 2, CS: 2, LE: 6, CE: 6); |
384 | |
385 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
386 | |
387 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
388 | EXPECT_EQ(2, std::distance(FunctionRecords.begin(), FunctionRecords.end())); |
389 | for (const auto &FunctionRecord : FunctionRecords) { |
390 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
391 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
392 | ASSERT_EQ(2U, Segments.size()); |
393 | if (FunctionRecord.Name == "func1" ) { |
394 | EXPECT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]); |
395 | EXPECT_EQ(CoverageSegment(5, 5, false), Segments[1]); |
396 | } else { |
397 | ASSERT_EQ("func2" , FunctionRecord.Name); |
398 | EXPECT_EQ(CoverageSegment(2, 2, 20, true), Segments[0]); |
399 | EXPECT_EQ(CoverageSegment(6, 6, false), Segments[1]); |
400 | } |
401 | } |
402 | } |
403 | |
404 | TEST_P(CoverageMappingTest, create_combined_regions) { |
405 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {1, 2, 3}}, Warn: Err); |
406 | startFunction(FuncName: "func1" , Hash: 0x1234); |
407 | |
408 | // Given regions which start at the same location, emit a segment for the |
409 | // last region. |
410 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 2, CE: 2); |
411 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 1, CS: 1, LE: 2, CE: 2); |
412 | addCMR(C: Counter::getCounter(CounterId: 2), File: "file1" , LS: 1, CS: 1, LE: 2, CE: 2); |
413 | |
414 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
415 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
416 | const auto &FunctionRecord = *FunctionRecords.begin(); |
417 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
418 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
419 | |
420 | ASSERT_EQ(2U, Segments.size()); |
421 | EXPECT_EQ(CoverageSegment(1, 1, 6, true), Segments[0]); |
422 | EXPECT_EQ(CoverageSegment(2, 2, false), Segments[1]); |
423 | } |
424 | |
425 | TEST_P(CoverageMappingTest, skipped_segments_have_no_count) { |
426 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {1}}, Warn: Err); |
427 | startFunction(FuncName: "func1" , Hash: 0x1234); |
428 | |
429 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 5, CE: 5); |
430 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 5, CS: 1, LE: 5, CE: 5, /*Skipped=*/true); |
431 | |
432 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
433 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
434 | const auto &FunctionRecord = *FunctionRecords.begin(); |
435 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
436 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
437 | |
438 | ASSERT_EQ(3U, Segments.size()); |
439 | EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]); |
440 | EXPECT_EQ(CoverageSegment(5, 1, true), Segments[1]); |
441 | EXPECT_EQ(CoverageSegment(5, 5, false), Segments[2]); |
442 | } |
443 | |
444 | TEST_P(CoverageMappingTest, multiple_regions_end_after_parent_ends) { |
445 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {1, 0}}, Warn: Err); |
446 | startFunction(FuncName: "func1" , Hash: 0x1234); |
447 | |
448 | // 1| F{ a{ |
449 | // 2| |
450 | // 3| a} b{ c{ |
451 | // 4| |
452 | // 5| b} |
453 | // 6| |
454 | // 7| c} d{ e{ |
455 | // 8| |
456 | // 9| d} e} F} |
457 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); // < F |
458 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 3, CE: 5); // < a |
459 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 3, CS: 5, LE: 5, CE: 4); // < b |
460 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 3, CS: 5, LE: 7, CE: 3); // < c |
461 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 7, CS: 3, LE: 9, CE: 2); // < d |
462 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 7, CS: 7, LE: 9, CE: 7); // < e |
463 | |
464 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
465 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
466 | const auto &FunctionRecord = *FunctionRecords.begin(); |
467 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
468 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
469 | |
470 | // Old output (not sorted or unique): |
471 | // Segment at 1:1 with count 1 |
472 | // Segment at 1:1 with count 1 |
473 | // Segment at 3:5 with count 1 |
474 | // Segment at 3:5 with count 0 |
475 | // Segment at 3:5 with count 1 |
476 | // Segment at 5:4 with count 0 |
477 | // Segment at 7:3 with count 1 |
478 | // Segment at 7:3 with count 0 |
479 | // Segment at 7:7 with count 0 |
480 | // Segment at 9:7 with count 0 |
481 | // Segment at 9:2 with count 1 |
482 | // Top level segment at 9:9 |
483 | |
484 | // New output (sorted and unique): |
485 | // Segment at 1:1 (count = 1), RegionEntry |
486 | // Segment at 3:5 (count = 1), RegionEntry |
487 | // Segment at 5:4 (count = 0) |
488 | // Segment at 7:3 (count = 0), RegionEntry |
489 | // Segment at 7:7 (count = 0), RegionEntry |
490 | // Segment at 9:2 (count = 0) |
491 | // Segment at 9:7 (count = 1) |
492 | // Segment at 9:9 (count = 0), Skipped |
493 | |
494 | ASSERT_EQ(8U, Segments.size()); |
495 | EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]); |
496 | EXPECT_EQ(CoverageSegment(3, 5, 1, true), Segments[1]); |
497 | EXPECT_EQ(CoverageSegment(5, 4, 0, false), Segments[2]); |
498 | EXPECT_EQ(CoverageSegment(7, 3, 0, true), Segments[3]); |
499 | EXPECT_EQ(CoverageSegment(7, 7, 0, true), Segments[4]); |
500 | EXPECT_EQ(CoverageSegment(9, 2, 0, false), Segments[5]); |
501 | EXPECT_EQ(CoverageSegment(9, 7, 1, false), Segments[6]); |
502 | EXPECT_EQ(CoverageSegment(9, 9, false), Segments[7]); |
503 | } |
504 | |
505 | TEST_P(CoverageMappingTest, multiple_completed_segments_at_same_loc) { |
506 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {0, 1, 2}}, Warn: Err); |
507 | startFunction(FuncName: "func1" , Hash: 0x1234); |
508 | |
509 | // PR35495 |
510 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 2, CS: 1, LE: 18, CE: 2); |
511 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 8, CS: 10, LE: 14, CE: 6); |
512 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 8, CS: 12, LE: 14, CE: 6); |
513 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 9, CS: 1, LE: 14, CE: 6); |
514 | addCMR(C: Counter::getCounter(CounterId: 2), File: "file1" , LS: 11, CS: 13, LE: 11, CE: 14); |
515 | |
516 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
517 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
518 | const auto &FunctionRecord = *FunctionRecords.begin(); |
519 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
520 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
521 | |
522 | ASSERT_EQ(7U, Segments.size()); |
523 | EXPECT_EQ(CoverageSegment(2, 1, 1, true), Segments[0]); |
524 | EXPECT_EQ(CoverageSegment(8, 10, 0, true), Segments[1]); |
525 | EXPECT_EQ(CoverageSegment(8, 12, 0, true), Segments[2]); |
526 | EXPECT_EQ(CoverageSegment(9, 1, 1, true), Segments[3]); |
527 | EXPECT_EQ(CoverageSegment(11, 13, 2, true), Segments[4]); |
528 | // Use count=1 (from 9:1 -> 14:6), not count=0 (from 8:12 -> 14:6). |
529 | EXPECT_EQ(CoverageSegment(11, 14, 1, false), Segments[5]); |
530 | EXPECT_EQ(CoverageSegment(18, 2, false), Segments[6]); |
531 | } |
532 | |
533 | TEST_P(CoverageMappingTest, dont_emit_redundant_segments) { |
534 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {1, 1}}, Warn: Err); |
535 | startFunction(FuncName: "func1" , Hash: 0x1234); |
536 | |
537 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 4, CE: 4); |
538 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 2, CS: 2, LE: 5, CE: 5); |
539 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 3, CS: 3, LE: 6, CE: 6); |
540 | |
541 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
542 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
543 | const auto &FunctionRecord = *FunctionRecords.begin(); |
544 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
545 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
546 | |
547 | ASSERT_EQ(5U, Segments.size()); |
548 | EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]); |
549 | EXPECT_EQ(CoverageSegment(2, 2, 1, true), Segments[1]); |
550 | EXPECT_EQ(CoverageSegment(3, 3, 1, true), Segments[2]); |
551 | EXPECT_EQ(CoverageSegment(4, 4, 1, false), Segments[3]); |
552 | // A closing segment starting at 5:5 would be redundant: it would have the |
553 | // same count as the segment starting at 4:4, and has all the same metadata. |
554 | EXPECT_EQ(CoverageSegment(6, 6, false), Segments[4]); |
555 | } |
556 | |
557 | TEST_P(CoverageMappingTest, dont_emit_closing_segment_at_new_region_start) { |
558 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {1}}, Warn: Err); |
559 | startFunction(FuncName: "func1" , Hash: 0x1234); |
560 | |
561 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 6, CE: 5); |
562 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 2, CS: 2, LE: 6, CE: 5); |
563 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 3, CS: 3, LE: 6, CE: 5); |
564 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 6, CS: 5, LE: 7, CE: 7); |
565 | |
566 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
567 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
568 | const auto &FunctionRecord = *FunctionRecords.begin(); |
569 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
570 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
571 | |
572 | ASSERT_EQ(5U, Segments.size()); |
573 | EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]); |
574 | EXPECT_EQ(CoverageSegment(2, 2, 1, true), Segments[1]); |
575 | EXPECT_EQ(CoverageSegment(3, 3, 1, true), Segments[2]); |
576 | EXPECT_EQ(CoverageSegment(6, 5, 1, true), Segments[3]); |
577 | // The old segment builder would get this wrong by emitting multiple segments |
578 | // which start at 6:5 (a few of which were skipped segments). We should just |
579 | // get a segment for the region entry. |
580 | EXPECT_EQ(CoverageSegment(7, 7, false), Segments[4]); |
581 | } |
582 | |
583 | TEST_P(CoverageMappingTest, handle_consecutive_regions_with_zero_length) { |
584 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {1, 2}}, Warn: Err); |
585 | startFunction(FuncName: "func1" , Hash: 0x1234); |
586 | |
587 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 1, CE: 1); |
588 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 1, CS: 1, LE: 1, CE: 1); |
589 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 1, CE: 1); |
590 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 1, CS: 1, LE: 1, CE: 1); |
591 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 1, CE: 1); |
592 | |
593 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
594 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
595 | const auto &FunctionRecord = *FunctionRecords.begin(); |
596 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
597 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
598 | |
599 | ASSERT_EQ(1U, Segments.size()); |
600 | EXPECT_EQ(CoverageSegment(1, 1, true), Segments[0]); |
601 | // We need to get a skipped segment starting at 1:1. In this case there is |
602 | // also a region entry at 1:1. |
603 | } |
604 | |
605 | TEST_P(CoverageMappingTest, handle_sandwiched_zero_length_region) { |
606 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {2, 1}}, Warn: Err); |
607 | startFunction(FuncName: "func1" , Hash: 0x1234); |
608 | |
609 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 5, LE: 4, CE: 4); |
610 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 1, CS: 9, LE: 1, CE: 50); |
611 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 2, CS: 7, LE: 2, CE: 34); |
612 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 3, CS: 5, LE: 3, CE: 21); |
613 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 3, CS: 21, LE: 3, CE: 21); |
614 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 4, CS: 12, LE: 4, CE: 17); |
615 | |
616 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
617 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
618 | const auto &FunctionRecord = *FunctionRecords.begin(); |
619 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
620 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
621 | |
622 | ASSERT_EQ(10U, Segments.size()); |
623 | EXPECT_EQ(CoverageSegment(1, 5, 2, true), Segments[0]); |
624 | EXPECT_EQ(CoverageSegment(1, 9, 1, true), Segments[1]); |
625 | EXPECT_EQ(CoverageSegment(1, 50, 2, false), Segments[2]); |
626 | EXPECT_EQ(CoverageSegment(2, 7, 1, true), Segments[3]); |
627 | EXPECT_EQ(CoverageSegment(2, 34, 2, false), Segments[4]); |
628 | EXPECT_EQ(CoverageSegment(3, 5, 1, true), Segments[5]); |
629 | EXPECT_EQ(CoverageSegment(3, 21, 2, true), Segments[6]); |
630 | // Handle the zero-length region by creating a segment with its predecessor's |
631 | // count (i.e the count from 1:5 -> 4:4). |
632 | EXPECT_EQ(CoverageSegment(4, 4, false), Segments[7]); |
633 | // The area between 4:4 and 4:12 is skipped. |
634 | EXPECT_EQ(CoverageSegment(4, 12, 1, true), Segments[8]); |
635 | EXPECT_EQ(CoverageSegment(4, 17, false), Segments[9]); |
636 | } |
637 | |
638 | TEST_P(CoverageMappingTest, handle_last_completed_region) { |
639 | ProfileWriter.addRecord(I: {"func1" , 0x1234, {1, 2, 3, 4}}, Warn: Err); |
640 | startFunction(FuncName: "func1" , Hash: 0x1234); |
641 | |
642 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 8, CE: 8); |
643 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 2, CS: 2, LE: 5, CE: 5); |
644 | addCMR(C: Counter::getCounter(CounterId: 2), File: "file1" , LS: 3, CS: 3, LE: 4, CE: 4); |
645 | addCMR(C: Counter::getCounter(CounterId: 3), File: "file1" , LS: 6, CS: 6, LE: 7, CE: 7); |
646 | |
647 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
648 | const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); |
649 | const auto &FunctionRecord = *FunctionRecords.begin(); |
650 | CoverageData Data = LoadedCoverage->getCoverageForFunction(Function: FunctionRecord); |
651 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
652 | |
653 | ASSERT_EQ(8U, Segments.size()); |
654 | EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]); |
655 | EXPECT_EQ(CoverageSegment(2, 2, 2, true), Segments[1]); |
656 | EXPECT_EQ(CoverageSegment(3, 3, 3, true), Segments[2]); |
657 | EXPECT_EQ(CoverageSegment(4, 4, 2, false), Segments[3]); |
658 | EXPECT_EQ(CoverageSegment(5, 5, 1, false), Segments[4]); |
659 | EXPECT_EQ(CoverageSegment(6, 6, 4, true), Segments[5]); |
660 | EXPECT_EQ(CoverageSegment(7, 7, 1, false), Segments[6]); |
661 | EXPECT_EQ(CoverageSegment(8, 8, false), Segments[7]); |
662 | } |
663 | |
664 | TEST_P(CoverageMappingTest, expansion_gets_first_counter) { |
665 | startFunction(FuncName: "func" , Hash: 0x1234); |
666 | addCMR(C: Counter::getCounter(CounterId: 1), File: "foo" , LS: 10, CS: 1, LE: 10, CE: 2); |
667 | // This starts earlier in "foo", so the expansion should get its counter. |
668 | addCMR(C: Counter::getCounter(CounterId: 2), File: "foo" , LS: 1, CS: 1, LE: 20, CE: 1); |
669 | addExpansionCMR(File: "bar" , ExpandedFile: "foo" , LS: 3, CS: 3, LE: 3, CE: 3); |
670 | |
671 | writeAndReadCoverageRegions(); |
672 | ASSERT_EQ(1u, OutputFunctions.size()); |
673 | OutputFunctionCoverageData &Output = OutputFunctions.back(); |
674 | |
675 | ASSERT_EQ(CounterMappingRegion::ExpansionRegion, Output.Regions[2].Kind); |
676 | ASSERT_EQ(Counter::getCounter(2), Output.Regions[2].Count); |
677 | ASSERT_EQ(3U, Output.Regions[2].LineStart); |
678 | } |
679 | |
680 | TEST_P(CoverageMappingTest, basic_coverage_iteration) { |
681 | ProfileWriter.addRecord(I: {"func" , 0x1234, {30, 20, 10, 0}}, Warn: Err); |
682 | |
683 | startFunction(FuncName: "func" , Hash: 0x1234); |
684 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
685 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 1, CS: 1, LE: 4, CE: 7); |
686 | addCMR(C: Counter::getCounter(CounterId: 2), File: "file1" , LS: 5, CS: 8, LE: 9, CE: 1); |
687 | addCMR(C: Counter::getCounter(CounterId: 3), File: "file1" , LS: 10, CS: 10, LE: 11, CE: 11); |
688 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
689 | |
690 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: "file1" ); |
691 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
692 | ASSERT_EQ(7U, Segments.size()); |
693 | ASSERT_EQ(CoverageSegment(1, 1, 20, true), Segments[0]); |
694 | ASSERT_EQ(CoverageSegment(4, 7, 30, false), Segments[1]); |
695 | ASSERT_EQ(CoverageSegment(5, 8, 10, true), Segments[2]); |
696 | ASSERT_EQ(CoverageSegment(9, 1, 30, false), Segments[3]); |
697 | ASSERT_EQ(CoverageSegment(9, 9, false), Segments[4]); |
698 | ASSERT_EQ(CoverageSegment(10, 10, 0, true), Segments[5]); |
699 | ASSERT_EQ(CoverageSegment(11, 11, false), Segments[6]); |
700 | } |
701 | |
702 | TEST_P(CoverageMappingTest, test_line_coverage_iterator) { |
703 | ProfileWriter.addRecord(I: {"func" , 0x1234, {30, 20, 10, 0}}, Warn: Err); |
704 | |
705 | startFunction(FuncName: "func" , Hash: 0x1234); |
706 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
707 | addSkipped(File: "file1" , LS: 1, CS: 3, LE: 1, CE: 8); // skipped region inside previous block |
708 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 1, CS: 1, LE: 4, CE: 7); |
709 | addSkipped(File: "file1" , LS: 4, CS: 1, LE: 4, CE: 20); // skipped line |
710 | addCMR(C: Counter::getCounter(CounterId: 2), File: "file1" , LS: 5, CS: 8, LE: 9, CE: 1); |
711 | addSkipped(File: "file1" , LS: 10, CS: 1, LE: 12, |
712 | CE: 20); // skipped region which contains next region |
713 | addCMR(C: Counter::getCounter(CounterId: 3), File: "file1" , LS: 10, CS: 10, LE: 11, CE: 11); |
714 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
715 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: "file1" ); |
716 | |
717 | unsigned Line = 0; |
718 | const unsigned LineCounts[] = {20, 20, 20, 0, 30, 10, 10, 10, 10, 0, 0, 0, 0}; |
719 | const bool MappedLines[] = {1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0}; |
720 | ASSERT_EQ(std::size(LineCounts), std::size(MappedLines)); |
721 | |
722 | for (const auto &LCS : getLineCoverageStats(CD: Data)) { |
723 | ASSERT_LT(Line, std::size(LineCounts)); |
724 | ASSERT_LT(Line, std::size(MappedLines)); |
725 | |
726 | ASSERT_EQ(Line + 1, LCS.getLine()); |
727 | errs() << "Line: " << Line + 1 << ", count = " << LCS.getExecutionCount() |
728 | << ", mapped = " << LCS.isMapped() << "\n" ; |
729 | ASSERT_EQ(LineCounts[Line], LCS.getExecutionCount()); |
730 | ASSERT_EQ(MappedLines[Line], LCS.isMapped()); |
731 | ++Line; |
732 | } |
733 | ASSERT_EQ(12U, Line); |
734 | |
735 | // Check that operator->() works / compiles. |
736 | ASSERT_EQ(1U, LineCoverageIterator(Data)->getLine()); |
737 | } |
738 | |
739 | TEST_P(CoverageMappingTest, uncovered_function) { |
740 | startFunction(FuncName: "func" , Hash: 0x1234); |
741 | addCMR(C: Counter::getZero(), File: "file1" , LS: 1, CS: 2, LE: 3, CE: 4); |
742 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
743 | |
744 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: "file1" ); |
745 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
746 | ASSERT_EQ(2U, Segments.size()); |
747 | ASSERT_EQ(CoverageSegment(1, 2, 0, true), Segments[0]); |
748 | ASSERT_EQ(CoverageSegment(3, 4, false), Segments[1]); |
749 | } |
750 | |
751 | TEST_P(CoverageMappingTest, uncovered_function_with_mapping) { |
752 | startFunction(FuncName: "func" , Hash: 0x1234); |
753 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
754 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 1, CS: 1, LE: 4, CE: 7); |
755 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
756 | |
757 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: "file1" ); |
758 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
759 | ASSERT_EQ(3U, Segments.size()); |
760 | ASSERT_EQ(CoverageSegment(1, 1, 0, true), Segments[0]); |
761 | ASSERT_EQ(CoverageSegment(4, 7, 0, false), Segments[1]); |
762 | ASSERT_EQ(CoverageSegment(9, 9, false), Segments[2]); |
763 | } |
764 | |
765 | TEST_P(CoverageMappingTest, combine_regions) { |
766 | ProfileWriter.addRecord(I: {"func" , 0x1234, {10, 20, 30}}, Warn: Err); |
767 | |
768 | startFunction(FuncName: "func" , Hash: 0x1234); |
769 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
770 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 3, CS: 3, LE: 4, CE: 4); |
771 | addCMR(C: Counter::getCounter(CounterId: 2), File: "file1" , LS: 3, CS: 3, LE: 4, CE: 4); |
772 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
773 | |
774 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: "file1" ); |
775 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
776 | ASSERT_EQ(4U, Segments.size()); |
777 | ASSERT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]); |
778 | ASSERT_EQ(CoverageSegment(3, 3, 50, true), Segments[1]); |
779 | ASSERT_EQ(CoverageSegment(4, 4, 10, false), Segments[2]); |
780 | ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]); |
781 | } |
782 | |
783 | TEST_P(CoverageMappingTest, restore_combined_counter_after_nested_region) { |
784 | ProfileWriter.addRecord(I: {"func" , 0x1234, {10, 20, 40}}, Warn: Err); |
785 | |
786 | startFunction(FuncName: "func" , Hash: 0x1234); |
787 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
788 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
789 | addCMR(C: Counter::getCounter(CounterId: 2), File: "file1" , LS: 3, CS: 3, LE: 5, CE: 5); |
790 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
791 | |
792 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: "file1" ); |
793 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
794 | ASSERT_EQ(4U, Segments.size()); |
795 | EXPECT_EQ(CoverageSegment(1, 1, 30, true), Segments[0]); |
796 | EXPECT_EQ(CoverageSegment(3, 3, 40, true), Segments[1]); |
797 | EXPECT_EQ(CoverageSegment(5, 5, 30, false), Segments[2]); |
798 | EXPECT_EQ(CoverageSegment(9, 9, false), Segments[3]); |
799 | } |
800 | |
801 | // If CodeRegions and ExpansionRegions cover the same area, |
802 | // only counts of CodeRegions should be used. |
803 | TEST_P(CoverageMappingTest, dont_combine_expansions) { |
804 | ProfileWriter.addRecord(I: {"func" , 0x1234, {10, 20}}, Warn: Err); |
805 | ProfileWriter.addRecord(I: {"func" , 0x1234, {0, 0}}, Warn: Err); |
806 | |
807 | startFunction(FuncName: "func" , Hash: 0x1234); |
808 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
809 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file1" , LS: 3, CS: 3, LE: 4, CE: 4); |
810 | addCMR(C: Counter::getCounter(CounterId: 1), File: "include1" , LS: 6, CS: 6, LE: 7, CE: 7); |
811 | addExpansionCMR(File: "file1" , ExpandedFile: "include1" , LS: 3, CS: 3, LE: 4, CE: 4); |
812 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
813 | |
814 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: "file1" ); |
815 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
816 | ASSERT_EQ(4U, Segments.size()); |
817 | ASSERT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]); |
818 | ASSERT_EQ(CoverageSegment(3, 3, 20, true), Segments[1]); |
819 | ASSERT_EQ(CoverageSegment(4, 4, 10, false), Segments[2]); |
820 | ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]); |
821 | } |
822 | |
823 | // If an area is covered only by ExpansionRegions, they should be combinated. |
824 | TEST_P(CoverageMappingTest, combine_expansions) { |
825 | ProfileWriter.addRecord(I: {"func" , 0x1234, {2, 3, 7}}, Warn: Err); |
826 | |
827 | startFunction(FuncName: "func" , Hash: 0x1234); |
828 | addCMR(C: Counter::getCounter(CounterId: 1), File: "include1" , LS: 1, CS: 1, LE: 1, CE: 10); |
829 | addCMR(C: Counter::getCounter(CounterId: 2), File: "include2" , LS: 1, CS: 1, LE: 1, CE: 10); |
830 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file" , LS: 1, CS: 1, LE: 5, CE: 5); |
831 | addExpansionCMR(File: "file" , ExpandedFile: "include1" , LS: 3, CS: 1, LE: 3, CE: 5); |
832 | addExpansionCMR(File: "file" , ExpandedFile: "include2" , LS: 3, CS: 1, LE: 3, CE: 5); |
833 | |
834 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
835 | |
836 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: "file" ); |
837 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
838 | ASSERT_EQ(4U, Segments.size()); |
839 | EXPECT_EQ(CoverageSegment(1, 1, 2, true), Segments[0]); |
840 | EXPECT_EQ(CoverageSegment(3, 1, 10, true), Segments[1]); |
841 | EXPECT_EQ(CoverageSegment(3, 5, 2, false), Segments[2]); |
842 | EXPECT_EQ(CoverageSegment(5, 5, false), Segments[3]); |
843 | } |
844 | |
845 | // Test that counters not associated with any code regions are allowed. |
846 | TEST_P(CoverageMappingTest, non_code_region_counters) { |
847 | // No records in profdata |
848 | |
849 | startFunction(FuncName: "func" , Hash: 0x1234); |
850 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file" , LS: 1, CS: 1, LE: 5, CE: 5); |
851 | addCMR(C: Counter::getExpression(ExpressionId: 0), File: "file" , LS: 6, CS: 1, LE: 6, CE: 5); |
852 | addExpression(CE: CounterExpression( |
853 | CounterExpression::Add, Counter::getCounter(CounterId: 1), Counter::getCounter(CounterId: 2))); |
854 | |
855 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
856 | |
857 | std::vector<std::string> Names; |
858 | for (const auto &Func : LoadedCoverage->getCoveredFunctions()) { |
859 | Names.push_back(x: Func.Name); |
860 | ASSERT_EQ(2U, Func.CountedRegions.size()); |
861 | } |
862 | ASSERT_EQ(1U, Names.size()); |
863 | } |
864 | |
865 | // Test that MCDC bitmasks not associated with any code regions are allowed. |
866 | TEST_P(CoverageMappingTest, non_code_region_bitmask) { |
867 | // No records in profdata |
868 | |
869 | startFunction(FuncName: "func" , Hash: 0x1234); |
870 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file" , LS: 1, CS: 1, LE: 5, CE: 5); |
871 | addCMR(C: Counter::getCounter(CounterId: 1), File: "file" , LS: 1, CS: 1, LE: 5, CE: 5); |
872 | addCMR(C: Counter::getCounter(CounterId: 2), File: "file" , LS: 1, CS: 1, LE: 5, CE: 5); |
873 | addCMR(C: Counter::getCounter(CounterId: 3), File: "file" , LS: 1, CS: 1, LE: 5, CE: 5); |
874 | |
875 | addMCDCDecisionCMR(Mask: 0, NC: 2, File: "file" , LS: 7, CS: 1, LE: 7, CE: 6); |
876 | addMCDCBranchCMR(C1: Counter::getCounter(CounterId: 0), C2: Counter::getCounter(CounterId: 1), ID: 0, Conds: {-1, 1}, |
877 | File: "file" , LS: 7, CS: 2, LE: 7, CE: 3); |
878 | addMCDCBranchCMR(C1: Counter::getCounter(CounterId: 2), C2: Counter::getCounter(CounterId: 3), ID: 1, Conds: {-1, -1}, |
879 | File: "file" , LS: 7, CS: 4, LE: 7, CE: 5); |
880 | |
881 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
882 | |
883 | std::vector<std::string> Names; |
884 | for (const auto &Func : LoadedCoverage->getCoveredFunctions()) { |
885 | Names.push_back(x: Func.Name); |
886 | ASSERT_EQ(2U, Func.CountedBranchRegions.size()); |
887 | ASSERT_EQ(1U, Func.MCDCRecords.size()); |
888 | } |
889 | ASSERT_EQ(1U, Names.size()); |
890 | } |
891 | |
892 | // Test the order of MCDCDecision before Expansion |
893 | TEST_P(CoverageMappingTest, decision_before_expansion) { |
894 | startFunction(FuncName: "foo" , Hash: 0x1234); |
895 | addCMR(C: Counter::getCounter(CounterId: 0), File: "foo" , LS: 3, CS: 23, LE: 5, CE: 2); |
896 | |
897 | // This(4:11) was put after Expansion(4:11) before the fix |
898 | addMCDCDecisionCMR(Mask: 0, NC: 2, File: "foo" , LS: 4, CS: 11, LE: 4, CE: 20); |
899 | |
900 | addExpansionCMR(File: "foo" , ExpandedFile: "A" , LS: 4, CS: 11, LE: 4, CE: 12); |
901 | addExpansionCMR(File: "foo" , ExpandedFile: "B" , LS: 4, CS: 19, LE: 4, CE: 20); |
902 | addCMR(C: Counter::getCounter(CounterId: 0), File: "A" , LS: 1, CS: 14, LE: 1, CE: 17); |
903 | addCMR(C: Counter::getCounter(CounterId: 0), File: "A" , LS: 1, CS: 14, LE: 1, CE: 17); |
904 | addMCDCBranchCMR(C1: Counter::getCounter(CounterId: 0), C2: Counter::getCounter(CounterId: 1), ID: 0, Conds: {-1, 1}, |
905 | File: "A" , LS: 1, CS: 14, LE: 1, CE: 17); |
906 | addCMR(C: Counter::getCounter(CounterId: 1), File: "B" , LS: 1, CS: 14, LE: 1, CE: 17); |
907 | addMCDCBranchCMR(C1: Counter::getCounter(CounterId: 1), C2: Counter::getCounter(CounterId: 2), ID: 1, Conds: {-1, -1}, |
908 | File: "B" , LS: 1, CS: 14, LE: 1, CE: 17); |
909 | |
910 | // InputFunctionCoverageData::Regions is rewritten after the write. |
911 | auto InputRegions = InputFunctions.back().Regions; |
912 | |
913 | writeAndReadCoverageRegions(); |
914 | |
915 | const auto &OutputRegions = OutputFunctions.back().Regions; |
916 | |
917 | size_t N = ArrayRef(InputRegions).size(); |
918 | ASSERT_EQ(N, OutputRegions.size()); |
919 | for (size_t I = 0; I < N; ++I) { |
920 | ASSERT_EQ(InputRegions[I].Kind, OutputRegions[I].Kind); |
921 | ASSERT_EQ(InputRegions[I].FileID, OutputRegions[I].FileID); |
922 | ASSERT_EQ(InputRegions[I].ExpandedFileID, OutputRegions[I].ExpandedFileID); |
923 | ASSERT_EQ(InputRegions[I].startLoc(), OutputRegions[I].startLoc()); |
924 | ASSERT_EQ(InputRegions[I].endLoc(), OutputRegions[I].endLoc()); |
925 | } |
926 | } |
927 | |
928 | TEST_P(CoverageMappingTest, strip_filename_prefix) { |
929 | ProfileWriter.addRecord(I: {"file1:func" , 0x1234, {0}}, Warn: Err); |
930 | |
931 | startFunction(FuncName: "file1:func" , Hash: 0x1234); |
932 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
933 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
934 | |
935 | std::vector<std::string> Names; |
936 | for (const auto &Func : LoadedCoverage->getCoveredFunctions()) |
937 | Names.push_back(x: Func.Name); |
938 | ASSERT_EQ(1U, Names.size()); |
939 | ASSERT_EQ("func" , Names[0]); |
940 | } |
941 | |
942 | TEST_P(CoverageMappingTest, strip_unknown_filename_prefix) { |
943 | ProfileWriter.addRecord(I: {"<unknown>:func" , 0x1234, {0}}, Warn: Err); |
944 | |
945 | startFunction(FuncName: "<unknown>:func" , Hash: 0x1234); |
946 | addCMR(C: Counter::getCounter(CounterId: 0), File: "" , LS: 1, CS: 1, LE: 9, CE: 9); |
947 | EXPECT_THAT_ERROR(loadCoverageMapping(/*EmitFilenames=*/false), Succeeded()); |
948 | |
949 | std::vector<std::string> Names; |
950 | for (const auto &Func : LoadedCoverage->getCoveredFunctions()) |
951 | Names.push_back(x: Func.Name); |
952 | ASSERT_EQ(1U, Names.size()); |
953 | ASSERT_EQ("func" , Names[0]); |
954 | } |
955 | |
956 | TEST_P(CoverageMappingTest, dont_detect_false_instantiations) { |
957 | ProfileWriter.addRecord(I: {"foo" , 0x1234, {10}}, Warn: Err); |
958 | ProfileWriter.addRecord(I: {"bar" , 0x2345, {20}}, Warn: Err); |
959 | |
960 | startFunction(FuncName: "foo" , Hash: 0x1234); |
961 | addCMR(C: Counter::getCounter(CounterId: 0), File: "expanded" , LS: 1, CS: 1, LE: 1, CE: 10); |
962 | addExpansionCMR(File: "main" , ExpandedFile: "expanded" , LS: 4, CS: 1, LE: 4, CE: 5); |
963 | |
964 | startFunction(FuncName: "bar" , Hash: 0x2345); |
965 | addCMR(C: Counter::getCounter(CounterId: 0), File: "expanded" , LS: 1, CS: 1, LE: 1, CE: 10); |
966 | addExpansionCMR(File: "main" , ExpandedFile: "expanded" , LS: 9, CS: 1, LE: 9, CE: 5); |
967 | |
968 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
969 | |
970 | std::vector<InstantiationGroup> InstantiationGroups = |
971 | LoadedCoverage->getInstantiationGroups(Filename: "expanded" ); |
972 | ASSERT_TRUE(InstantiationGroups.empty()); |
973 | } |
974 | |
975 | TEST_P(CoverageMappingTest, load_coverage_for_expanded_file) { |
976 | ProfileWriter.addRecord(I: {"func" , 0x1234, {10}}, Warn: Err); |
977 | |
978 | startFunction(FuncName: "func" , Hash: 0x1234); |
979 | addCMR(C: Counter::getCounter(CounterId: 0), File: "expanded" , LS: 1, CS: 1, LE: 1, CE: 10); |
980 | addExpansionCMR(File: "main" , ExpandedFile: "expanded" , LS: 4, CS: 1, LE: 4, CE: 5); |
981 | |
982 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
983 | |
984 | CoverageData Data = LoadedCoverage->getCoverageForFile(Filename: "expanded" ); |
985 | std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); |
986 | ASSERT_EQ(2U, Segments.size()); |
987 | EXPECT_EQ(CoverageSegment(1, 1, 10, true), Segments[0]); |
988 | EXPECT_EQ(CoverageSegment(1, 10, false), Segments[1]); |
989 | } |
990 | |
991 | TEST_P(CoverageMappingTest, skip_duplicate_function_record) { |
992 | ProfileWriter.addRecord(I: {"func" , 0x1234, {1}}, Warn: Err); |
993 | |
994 | // This record should be loaded. |
995 | startFunction(FuncName: "func" , Hash: 0x1234); |
996 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
997 | |
998 | // This record should be loaded. |
999 | startFunction(FuncName: "func" , Hash: 0x1234); |
1000 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
1001 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file2" , LS: 1, CS: 1, LE: 9, CE: 9); |
1002 | |
1003 | // This record should be skipped. |
1004 | startFunction(FuncName: "func" , Hash: 0x1234); |
1005 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
1006 | |
1007 | // This record should be loaded. |
1008 | startFunction(FuncName: "func" , Hash: 0x1234); |
1009 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file2" , LS: 1, CS: 1, LE: 9, CE: 9); |
1010 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
1011 | |
1012 | // This record should be skipped. |
1013 | startFunction(FuncName: "func" , Hash: 0x1234); |
1014 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file1" , LS: 1, CS: 1, LE: 9, CE: 9); |
1015 | addCMR(C: Counter::getCounter(CounterId: 0), File: "file2" , LS: 1, CS: 1, LE: 9, CE: 9); |
1016 | |
1017 | EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); |
1018 | |
1019 | auto Funcs = LoadedCoverage->getCoveredFunctions(); |
1020 | unsigned NumFuncs = std::distance(first: Funcs.begin(), last: Funcs.end()); |
1021 | ASSERT_EQ(3U, NumFuncs); |
1022 | } |
1023 | |
1024 | INSTANTIATE_TEST_SUITE_P(ParameterizedCovMapTest, CoverageMappingTest, |
1025 | ::testing::Combine(::testing::Bool(), |
1026 | ::testing::Bool())); |
1027 | |
1028 | TEST(CoverageMappingTest, filename_roundtrip) { |
1029 | std::vector<std::string> Paths({"dir" , "a" , "b" , "c" , "d" , "e" }); |
1030 | |
1031 | for (bool Compress : {false, true}) { |
1032 | std::string EncodedFilenames; |
1033 | { |
1034 | raw_string_ostream OS(EncodedFilenames); |
1035 | CoverageFilenamesSectionWriter Writer(Paths); |
1036 | Writer.write(OS, Compress); |
1037 | } |
1038 | |
1039 | std::vector<std::string> ReadFilenames; |
1040 | RawCoverageFilenamesReader Reader(EncodedFilenames, ReadFilenames); |
1041 | EXPECT_THAT_ERROR(Reader.read(CovMapVersion::CurrentVersion), Succeeded()); |
1042 | |
1043 | ASSERT_EQ(ReadFilenames.size(), Paths.size()); |
1044 | for (unsigned I = 1; I < Paths.size(); ++I) { |
1045 | SmallString<256> P(Paths[0]); |
1046 | llvm::sys::path::append(path&: P, a: Paths[I]); |
1047 | ASSERT_EQ(ReadFilenames[I], P); |
1048 | } |
1049 | } |
1050 | } |
1051 | |
1052 | TEST(CoverageMappingTest, filename_compilation_dir) { |
1053 | std::vector<std::string> Paths({"dir" , "a" , "b" , "c" , "d" , "e" }); |
1054 | |
1055 | for (bool Compress : {false, true}) { |
1056 | std::string EncodedFilenames; |
1057 | { |
1058 | raw_string_ostream OS(EncodedFilenames); |
1059 | CoverageFilenamesSectionWriter Writer(Paths); |
1060 | Writer.write(OS, Compress); |
1061 | } |
1062 | |
1063 | StringRef CompilationDir = "out" ; |
1064 | std::vector<std::string> ReadFilenames; |
1065 | RawCoverageFilenamesReader Reader(EncodedFilenames, ReadFilenames, |
1066 | CompilationDir); |
1067 | EXPECT_THAT_ERROR(Reader.read(CovMapVersion::CurrentVersion), Succeeded()); |
1068 | |
1069 | ASSERT_EQ(ReadFilenames.size(), Paths.size()); |
1070 | for (unsigned I = 1; I < Paths.size(); ++I) { |
1071 | SmallString<256> P(CompilationDir); |
1072 | llvm::sys::path::append(path&: P, a: Paths[I]); |
1073 | ASSERT_EQ(ReadFilenames[I], P); |
1074 | } |
1075 | } |
1076 | } |
1077 | |
1078 | TEST(CoverageMappingTest, TVIdxBuilder) { |
1079 | // ((n0 && n3) || (n2 && n4) || (n1 && n5)) |
1080 | static const std::array<mcdc::ConditionIDs, 6> Branches = {._M_elems: { |
1081 | {2, 3}, |
1082 | {-1, 5}, |
1083 | {1, 4}, |
1084 | {2, -1}, |
1085 | {1, -1}, |
1086 | {-1, -1}, |
1087 | }}; |
1088 | int Offset = 1000; |
1089 | auto TheBuilder = mcdc::TVIdxBuilder( |
1090 | SmallVector<mcdc::ConditionIDs>(ArrayRef(Branches)), Offset); |
1091 | EXPECT_TRUE(TheBuilder.NumTestVectors < TheBuilder.HardMaxTVs); |
1092 | EXPECT_EQ(TheBuilder.Indices.size(), 6u); |
1093 | EXPECT_EQ(TheBuilder.NumTestVectors, 15); |
1094 | |
1095 | std::map<int, int> Decisions; |
1096 | for (unsigned I = 0; I < TheBuilder.Indices.size(); ++I) { |
1097 | struct Rec { |
1098 | int Width; |
1099 | std::array<int, 2> Indices; |
1100 | }; |
1101 | static const std::array<Rec, 6> IndicesRefs = {._M_elems: { |
1102 | {.Width: 1, .Indices: {0, 0}}, |
1103 | {.Width: 4, .Indices: {1000, 0}}, |
1104 | {.Width: 2, .Indices: {0, 0}}, |
1105 | {.Width: 1, .Indices: {1, 1014}}, |
1106 | {.Width: 2, .Indices: {2, 1012}}, |
1107 | {.Width: 4, .Indices: {1004, 1008}}, |
1108 | }}; |
1109 | EXPECT_EQ(TheBuilder.Indices[I], IndicesRefs[I].Indices); |
1110 | |
1111 | #ifndef NDEBUG |
1112 | const auto &Node = TheBuilder.SavedNodes[I]; |
1113 | EXPECT_EQ(Node.Width, IndicesRefs[I].Width); |
1114 | for (int C = 0; C < 2; ++C) { |
1115 | auto Index = TheBuilder.Indices[I][C]; |
1116 | if (Node.NextIDs[C] < 0) |
1117 | EXPECT_TRUE(Decisions.insert({Index, Node.Width}).second); |
1118 | } |
1119 | #endif |
1120 | } |
1121 | |
1122 | #ifndef NDEBUG |
1123 | int NextIdx = Offset; |
1124 | for (const auto [Index, Width] : Decisions) { |
1125 | EXPECT_EQ(Index, NextIdx); |
1126 | NextIdx += Width; |
1127 | } |
1128 | // The sum of Width(s) is NumTVs. |
1129 | EXPECT_EQ(NextIdx, Offset + TheBuilder.NumTestVectors); |
1130 | #endif |
1131 | } |
1132 | |
1133 | } // end anonymous namespace |
1134 | |