1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <cstdio>
17 #include <fstream>
18 #include <fcntl.h>
19
20 #include "ecmascript/dfx/hprof/heap_profiler_interface.h"
21 #include "ecmascript/dfx/hprof/heap_profiler.h"
22 #include "ecmascript/dfx/hprof/heap_snapshot_json_serializer.h"
23 #include "ecmascript/dfx/hprof/heap_snapshot.h"
24 #include "ecmascript/ecma_string.h"
25 #include "ecmascript/global_env.h"
26
27 #include "ecmascript/js_tagged_value.h"
28 #include "ecmascript/js_thread.h"
29 #include "ecmascript/mem/heap.h"
30 #include "ecmascript/tests/test_helper.h"
31 #include "ecmascript/dfx/hprof/file_stream.h"
32
33 using namespace panda::ecmascript;
34
35 namespace panda::ecmascript {
36 class TestProgress : public Progress {
37 public:
38 TestProgress() = default;
39 ~TestProgress() = default;
40
ReportProgress(int32_t done,int32_t total)41 void ReportProgress([[maybe_unused]] int32_t done, [[maybe_unused]] int32_t total) override {}
42 };
43
44 class TestStream : public Stream {
45 public:
46 TestStream() = default;
47 ~TestStream() = default;
48
EndOfStream()49 void EndOfStream() override {}
GetSize()50 int GetSize() override
51 {
52 static const int heapProfilerChunkSise = 100_KB;
53 return heapProfilerChunkSise;
54 }
WriteChunk(char * data,int32_t size)55 bool WriteChunk([[maybe_unused]] char *data, [[maybe_unused]] int32_t size) override
56 {
57 return true;
58 }
Good()59 bool Good() override
60 {
61 return testStream_.good();
62 }
63
UpdateHeapStats(HeapStat * updateData,int32_t count)64 void UpdateHeapStats([[maybe_unused]] HeapStat* updateData, [[maybe_unused]] int32_t count) override
65 {
66 }
67
UpdateLastSeenObjectId(int32_t lastSeenObjectId,int64_t timeStampUs)68 void UpdateLastSeenObjectId([[maybe_unused]] int32_t lastSeenObjectId, [[maybe_unused]]int64_t timeStampUs) override
69 {
70 }
71
Clear()72 void Clear()
73 {
74 testStream_.clear(std::ios::badbit);
75 }
76
77 private:
78 std::fstream testStream_;
79 };
80 }
81
82 namespace panda::test {
83 class HeapTrackerTest : public testing::Test {
84 public:
SetUpTestCase()85 static void SetUpTestCase()
86 {
87 GTEST_LOG_(INFO) << "SetUpTestCase";
88 }
89
TearDownTestCase()90 static void TearDownTestCase()
91 {
92 GTEST_LOG_(INFO) << "TearDownCase";
93 }
94
SetUp()95 void SetUp() override
96 {
97 TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
98 instance->SetEnableForceGC(false);
99 }
100
TearDown()101 void TearDown() override
102 {
103 TestHelper::DestroyEcmaVMWithScope(instance, scope);
104 }
105
106 EcmaVM *instance {nullptr};
107 EcmaHandleScope *scope {nullptr};
108 JSThread *thread {nullptr};
109 };
110
HWTEST_F_L0(HeapTrackerTest,HeapTracker)111 HWTEST_F_L0(HeapTrackerTest, HeapTracker)
112 {
113 [[maybe_unused]] EcmaHandleScope handleScope(thread);
114 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
115 heapProfile->StartHeapTracking(50);
116 sleep(1);
117 int count = 100;
118 while (count-- > 0) {
119 instance->GetFactory()->NewJSAsyncFuncObject();
120 }
121 sleep(1);
122 count = 100;
123 while (count-- > 0) {
124 instance->GetFactory()->NewJSSymbol();
125 }
126 sleep(1);
127 count = 100;
128 while (count-- > 0) {
129 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
130 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
131 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
132 }
133
134 // Create file test.heaptimeline
135 std::string fileName = "test.heaptimeline";
136 fstream outputString(fileName, std::ios::out);
137 outputString.close();
138 outputString.clear();
139
140 FileStream stream(fileName.c_str());
141 heapProfile->StopHeapTracking(&stream, nullptr);
142 HeapProfilerInterface::Destroy(instance);
143
144 // Check
145 fstream inputStream(fileName, std::ios::in);
146 std::string line;
147 std::string emptySample = "\"samples\":";
148 std::string firstSample = "\"samples\":[0, ";
149 uint32_t emptySize = emptySample.size();
150 bool isFind = false;
151 while (getline(inputStream, line)) {
152 if (line.substr(0U, emptySize) == emptySample) {
153 ASSERT_TRUE(line.substr(0, firstSample.size()) == firstSample);
154 isFind = true;
155 }
156 }
157 ASSERT_TRUE(isFind);
158
159 inputStream.close();
160 inputStream.clear();
161 std::remove(fileName.c_str());
162 }
163
HWTEST_F_L0(HeapTrackerTest,HeapTrackerTraceAllocation)164 HWTEST_F_L0(HeapTrackerTest, HeapTrackerTraceAllocation)
165 {
166 [[maybe_unused]] EcmaHandleScope handleScope(thread);
167 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
168 TestStream testStream;
169 heapProfile->StartHeapTracking(50, true, &testStream, true);
170 sleep(1);
171 int count = 100;
172 while (count-- > 0) {
173 instance->GetFactory()->NewJSAsyncFuncObject();
174 }
175 sleep(1);
176 count = 100;
177 while (count-- > 0) {
178 instance->GetFactory()->NewJSSymbol();
179 }
180 sleep(1);
181 count = 100;
182 while (count-- > 0) {
183 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
184 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
185 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
186 }
187
188 // Create file test.heaptimeline
189 std::string fileName = "test.heaptimeline";
190 fstream outputString(fileName, std::ios::out);
191 outputString.close();
192 outputString.clear();
193
194 FileStream stream(fileName.c_str());
195 TestProgress testProgress;
196 heapProfile->StopHeapTracking(&stream, &testProgress);
197 HeapProfilerInterface::Destroy(instance);
198
199 // Check
200 fstream inputStream(fileName, std::ios::in);
201 std::string line;
202 std::string emptyTraceFunctionInfo = "\"trace_function_infos\":[";
203 std::string firstTraceFunctionInfo = "\"trace_function_infos\":[0,";
204 uint32_t emptyTraceFunctionInfoSize = emptyTraceFunctionInfo.size();
205 bool traceFunctionInfoIsFind = false;
206 while (getline(inputStream, line)) {
207 if (line.substr(0U, emptyTraceFunctionInfoSize) == emptyTraceFunctionInfo) {
208 ASSERT_TRUE(line.substr(0, firstTraceFunctionInfo.size()) == firstTraceFunctionInfo);
209 traceFunctionInfoIsFind = true;
210 break;
211 }
212 }
213 ASSERT_TRUE(traceFunctionInfoIsFind);
214
215 inputStream.close();
216 inputStream.clear();
217 std::remove(fileName.c_str());
218 }
219
HWTEST_F_L0(HeapTrackerTest,DumpHeapSnapshot)220 HWTEST_F_L0(HeapTrackerTest, DumpHeapSnapshot)
221 {
222 [[maybe_unused]] EcmaHandleScope handleScope(thread);
223 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
224
225 sleep(1);
226 int count = 100;
227 while (count-- > 0) {
228 instance->GetFactory()->NewJSAsyncFuncObject();
229 }
230 sleep(1);
231 count = 100;
232 while (count-- > 0) {
233 instance->GetFactory()->NewJSSymbol();
234 }
235 sleep(1);
236 count = 100;
237 while (count-- > 0) {
238 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
239 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
240 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
241 }
242
243 // Create file test.heaptimeline
244 std::string fileName = "test.heapsnapshot";
245 fstream outputString(fileName, std::ios::out);
246 outputString.close();
247 outputString.clear();
248
249 FileStream stream(fileName.c_str());
250 TestProgress testProgress;
251 heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false);
252 HeapProfilerInterface::Destroy(instance);
253
254 // Check
255 fstream inputStream(fileName, std::ios::in);
256 std::string line;
257 std::string nodes = "\"nodes\":[";
258 std::string sample = "\"samples\":[]";
259 uint32_t nodesSize = nodes.size();
260 uint32_t sampleSize = sample.size();
261 bool isNodesFind = false;
262 bool isSampleFind = false;
263 while (getline(inputStream, line)) {
264 if (line.substr(0U, nodesSize) == nodes) {
265 isNodesFind = true;
266 }
267
268 if (line.substr(0U, sampleSize) == sample) {
269 isSampleFind = true;
270 }
271 }
272 ASSERT_TRUE(isNodesFind);
273 ASSERT_TRUE(isSampleFind);
274
275 inputStream.close();
276 inputStream.clear();
277 std::remove(fileName.c_str());
278 }
279
HWTEST_F_L0(HeapTrackerTest,HeapSnapshotBuildUp)280 HWTEST_F_L0(HeapTrackerTest, HeapSnapshotBuildUp)
281 {
282 bool isVmMode = true;
283 bool isPrivate = false;
284 bool traceAllocation = false;
285 bool captureNumericValue = false;
286 HeapProfiler heapProfiler(instance);
287 HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, captureNumericValue, traceAllocation,
288 heapProfiler.GetEntryIdMap(), instance->GetChunk());
289 EXPECT_TRUE(heapSnapshot.BuildUp());
290 }
291
HWTEST_F_L0(HeapTrackerTest,HeapSnapshotUpdateNode)292 HWTEST_F_L0(HeapTrackerTest, HeapSnapshotUpdateNode)
293 {
294 bool isVmMode = true;
295 bool isPrivate = false;
296 bool traceAllocation = false;
297 bool captureNumericValue = false;
298 HeapProfiler heapProfiler(instance);
299 HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, captureNumericValue, traceAllocation,
300 heapProfiler.GetEntryIdMap(), instance->GetChunk());
301 size_t beginNode = heapSnapshot.GetNodeCount();
302 heapSnapshot.UpdateNodes();
303 size_t endNode = heapSnapshot.GetNodeCount();
304 EXPECT_TRUE(beginNode != endNode);
305 }
306
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_001)307 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_001)
308 {
309 [[maybe_unused]] EcmaHandleScope handleScope(thread);
310 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
311
312 sleep(1);
313 int count = 100;
314 while (count-- > 0) {
315 instance->GetFactory()->NewJSAsyncFuncObject();
316 }
317 sleep(1);
318 count = 100;
319 while (count-- > 0) {
320 instance->GetFactory()->NewJSSymbol();
321 }
322 sleep(1);
323 count = 100;
324 while (count-- > 0) {
325 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
326 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
327 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
328 }
329
330 TestStream stream;
331 stream.Clear();
332 EXPECT_TRUE(!stream.Good());
333 TestProgress testProgress;
334 heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false);
335 HeapProfilerInterface::Destroy(instance);
336 }
337
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_002)338 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_002)
339 {
340 [[maybe_unused]] EcmaHandleScope handleScope(thread);
341 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
342
343 sleep(1);
344 int count = 100;
345 while (count-- > 0) {
346 instance->GetFactory()->NewJSAsyncFuncObject();
347 }
348 sleep(1);
349 count = 100;
350 while (count-- > 0) {
351 instance->GetFactory()->NewJSSymbol();
352 }
353 sleep(1);
354 count = 100;
355 while (count-- > 0) {
356 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
357 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
358 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
359 }
360
361 TestStream stream;
362 stream.Clear();
363 EXPECT_TRUE(!stream.Good());
364 TestProgress testProgress;
365 heapProfile->DumpHeapSnapshot(DumpFormat::BINARY, &stream, &testProgress, true, true, false);
366 HeapProfilerInterface::Destroy(instance);
367 }
368
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_003)369 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_003)
370 {
371 [[maybe_unused]] EcmaHandleScope handleScope(thread);
372 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
373
374 sleep(1);
375 int count = 100;
376 while (count-- > 0) {
377 instance->GetFactory()->NewJSAsyncFuncObject();
378 }
379 sleep(1);
380 count = 100;
381 while (count-- > 0) {
382 instance->GetFactory()->NewJSSymbol();
383 }
384 sleep(1);
385 count = 100;
386 while (count-- > 0) {
387 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
388 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
389 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
390 }
391
392 TestStream stream;
393 stream.Clear();
394 EXPECT_TRUE(!stream.Good());
395 TestProgress testProgress;
396 heapProfile->DumpHeapSnapshot(DumpFormat::OTHER, &stream, &testProgress, true, true, false);
397 HeapProfilerInterface::Destroy(instance);
398 }
399
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_004)400 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_004)
401 {
402 [[maybe_unused]] EcmaHandleScope handleScope(thread);
403 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
404
405 sleep(1);
406 int count = 100;
407 while (count-- > 0) {
408 instance->GetFactory()->NewJSAsyncFuncObject();
409 }
410 sleep(1);
411 count = 100;
412 while (count-- > 0) {
413 instance->GetFactory()->NewJSSymbol();
414 }
415 sleep(1);
416 count = 100;
417 while (count-- > 0) {
418 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
419 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
420 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
421 }
422
423 TestStream stream;
424 stream.Clear();
425 EXPECT_TRUE(!stream.Good());
426 TestProgress testProgress;
427 DumpFormat dumFormat = static_cast<DumpFormat>(5);
428 heapProfile->DumpHeapSnapshot(dumFormat, &stream, &testProgress, true, true, false);
429 HeapProfilerInterface::Destroy(instance);
430 }
431
HWTEST_F_L0(HeapTrackerTest,FileDescriptorStreamEndOfStream)432 HWTEST_F_L0(HeapTrackerTest, FileDescriptorStreamEndOfStream)
433 {
434 int fd = 3;
435 FileDescriptorStream fileStream(fd);
436 EXPECT_TRUE(fileStream.Good());
437 fileStream.EndOfStream();
438 }
439
HWTEST_F_L0(HeapTrackerTest,StreamWriterEnd)440 HWTEST_F_L0(HeapTrackerTest, StreamWriterEnd)
441 {
442 [[maybe_unused]] EcmaHandleScope handleScope(thread);
443 HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
444
445 sleep(1);
446 int count = 100;
447 while (count-- > 0) {
448 instance->GetFactory()->NewJSAsyncFuncObject();
449 }
450 sleep(1);
451 count = 100;
452 while (count-- > 0) {
453 instance->GetFactory()->NewJSSymbol();
454 }
455 sleep(1);
456 count = 100;
457 while (count-- > 0) {
458 JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
459 JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
460 instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
461 }
462
463 // Create file test.heaptimeline
464 std::string fileName = "test.heapsnapshot";
465 fstream outputString(fileName, std::ios::out);
466 outputString.close();
467 outputString.clear();
468
469 FileStream stream(fileName.c_str());
470 CVector<HeapStat> statsBuffer;
471 statsBuffer.emplace_back(1, 2, 4);
472 stream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
473 stream.UpdateLastSeenObjectId(1, 1677567644913058);
474
475 TestProgress testProgress;
476 heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false);
477 StreamWriter streamWriter(&stream);
478 streamWriter.End();
479 HeapProfilerInterface::Destroy(instance);
480
481 // Check
482 fstream inputStream(fileName, std::ios::in);
483 std::string line;
484 std::string nodes = "\"nodes\":[";
485 std::string sample = "\"samples\":[]";
486 uint32_t nodesSize = nodes.size();
487 uint32_t sampleSize = sample.size();
488 bool isNodesFind = false;
489 bool isSampleFind = false;
490 while (getline(inputStream, line)) {
491 if (line.substr(0U, nodesSize) == nodes) {
492 isNodesFind = true;
493 }
494
495 if (line.substr(0U, sampleSize) == sample) {
496 isSampleFind = true;
497 }
498 }
499 ASSERT_TRUE(isNodesFind);
500 ASSERT_TRUE(isSampleFind);
501
502 inputStream.close();
503 inputStream.clear();
504 std::remove(fileName.c_str());
505 }
506
HWTEST_F_L0(HeapTrackerTest,GetStringByKey)507 HWTEST_F_L0(HeapTrackerTest, GetStringByKey)
508 {
509 StringKey key = static_cast<StringKey>(2);
510 StringHashMap stringHashMap(instance);
511 CString *hashMap = stringHashMap.GetStringByKey(key);
512 EXPECT_TRUE(hashMap == nullptr);
513 }
514
HWTEST_F_L0(HeapTrackerTest,FormatString)515 HWTEST_F_L0(HeapTrackerTest, FormatString)
516 {
517 bool isVmMode = true;
518 bool isPrivate = false;
519 bool traceAllocation = false;
520 bool captureNumericValue = false;
521 HeapProfiler heapProfiler(instance);
522 HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, captureNumericValue, traceAllocation,
523 heapProfiler.GetEntryIdMap(), instance->GetChunk());
524
525 StringHashMap stringHashMap(instance);
526 CString ret = "H\"e\rl\nl\\o\t W\fo\31rld!";
527 stringHashMap.GetString(ret);
528 StringKey retKey = std::hash<std::string>{} (std::string(ret));
529
530 CString *tmpResult = nullptr;
531 tmpResult = stringHashMap.GetStringByKey(retKey);
532 EXPECT_TRUE(tmpResult != nullptr);
533 EXPECT_TRUE(*tmpResult == "H`e`l`l`o` W`o`rld!");
534 }
535
HWTEST_F_L0(HeapTrackerTest,FileDescriptorStreamWriteChunk)536 HWTEST_F_L0(HeapTrackerTest, FileDescriptorStreamWriteChunk)
537 {
538 int32_t fd = -1;
539 FileDescriptorStream testFileStream(fd);
540 CVector<HeapStat> statsBuffer;
541 statsBuffer.emplace_back(1, 2, 4);
542 testFileStream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
543 testFileStream.UpdateLastSeenObjectId(1, 1677567644913058);
544 testFileStream.GetSize();
545 std::string testString = "Hello!";
546 int strSize = testString.size();
547 bool isFileStream = testFileStream.WriteChunk(testString.data(), strSize);
548 EXPECT_TRUE(!isFileStream);
549
550 fd = 5;
551 FileDescriptorStream tmpFileStream(fd);
552 tmpFileStream.Good();
553 testString = "Hello!";
554 strSize = testString.size();
555 isFileStream = tmpFileStream.WriteChunk(testString.data(), strSize);
556 close(fd);
557 EXPECT_TRUE(!isFileStream);
558
559 std::string fileName = "test.StreamWriteChunk";
560 fd = open(fileName.c_str(), O_RDONLY);
561 if (fd < 0) {
562 fd = open(fileName.c_str(), O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IROTH);
563 }
564 FileDescriptorStream fileStream(fd);
565 testString = "Hello!";
566 strSize = testString.size();
567 isFileStream = fileStream.WriteChunk(testString.data(), strSize);
568 EXPECT_TRUE(isFileStream);
569 std::remove(fileName.c_str());
570 close(fd);
571 }
572
HWTEST_F_L0(HeapTrackerTest,AddNodeToTree)573 HWTEST_F_L0(HeapTrackerTest, AddNodeToTree)
574 {
575 CVector<uint32_t> traceNodeIndex;
576 for (int i = 0; i < 3; i++) {
577 traceNodeIndex.push_back(i + 1);
578 }
579 TraceTree traceTree;
580 TraceNode *traceNode = traceTree.AddNodeToTree(traceNodeIndex);
581 EXPECT_TRUE(traceNode != nullptr);
582 }
583
HWTEST_F_L0(HeapTrackerTest,FindOrAddChild)584 HWTEST_F_L0(HeapTrackerTest, FindOrAddChild)
585 {
586 TraceTree traceTree;
587 uint32_t index = 1;
588 TraceNode traceNode(&traceTree, index);
589 TraceNode *node = traceNode.FindOrAddChild(index);
590 EXPECT_TRUE(node->GetNodeIndex() == 1);
591
592 TraceNode *tmpNode = traceNode.FindOrAddChild(2);
593 EXPECT_TRUE(tmpNode->GetNodeIndex() == 2);
594 }
595 } // namespace panda::test
596