2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2019,2020, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Tests for legacy symbol table and replacement.
39 * \author Paul Bauer <paul.bauer.q@gmail.com>
43 #include "gromacs/topology/symtab.h"
47 #include <gtest/gtest.h>
49 #include "gromacs/utility/arrayref.h"
50 #include "gromacs/utility/exceptions.h"
51 #include "gromacs/utility/inmemoryserializer.h"
52 #include "gromacs/utility/strconvert.h"
53 #include "gromacs/utility/stringutil.h"
54 #include "gromacs/utility/textreader.h"
56 #include "testutils/refdata.h"
57 #include "testutils/testfilemanager.h"
65 class StringTableTest
: public ::testing::Test
69 //! Get handle to symbol table.
70 StringTableBuilder
& builder() { return stringTableBuilder_
; }
72 * Check human readable format of the table.
73 * \todo change when table writing to users is done also with serializer.
74 * \parm[in] table The string table to check the serialization.
76 void checkTable(const StringTable
& table
);
79 //! Get reference checker using lazy initialization
80 TestReferenceChecker
* checker()
84 checker_
= std::make_unique
<TestReferenceChecker
>(data_
.rootChecker());
86 return checker_
.get();
88 //! Symbol table for testing purposes.
89 StringTableBuilder stringTableBuilder_
;
90 //! Handler for reference data.
91 TestReferenceData data_
;
92 //! Handler for checking reference data.
93 std::unique_ptr
<TestReferenceChecker
> checker_
;
96 void StringTableTest::checkTable(const StringTable
& table
)
98 TestFileManager files
;
99 std::string
filename(files
.getTemporaryFilePath("table.txt"));
100 FILE* fp
= fopen(filename
.c_str(), "w");
101 table
.printStringTableStorageToFile(fp
, 4, "Test title");
103 const std::string text
= TextReader::readFileToString(filename
);
104 checker()->checkTextBlock(text
, "Output");
108 * Check that symbols obtained from symtab compare correctly.
110 * Helper function to find out if two entries obtained by a symtab lookup
111 * are equivalent or not, according to testing criteria.
112 * Checks that the indices match before finalizing storage.
114 * \param[in] builder StringTableBuilder table that contains the entries to validate.
115 * \param[in] firstSymbol Handle into \p builder obtained from placing string in \p builder.
116 * \param[in] otherSymbol Other handle from obtained from separate string deposit.
117 * \param[in] expectedOutcome If the handles should result in equal entries or not.
119 static void compareDifferentIndices(const StringTableBuilder
& builder
,
120 const StringTableEntry
& firstSymbol
,
121 const StringTableEntry
& otherSymbol
,
122 bool expectedOutcome
)
124 EXPECT_EQ(expectedOutcome
, (firstSymbol
== otherSymbol
));
125 auto firstIndex
= builder
.findEntryByName(*firstSymbol
);
126 auto otherIndex
= builder
.findEntryByName(*otherSymbol
);
127 EXPECT_EQ(expectedOutcome
, (firstIndex
== otherIndex
))
128 << "Expected was " << expectedOutcome
<< " firstIndex is " << firstIndex
129 << " otherIndex is " << otherIndex
;
133 * Helper to obtain the integer index from an entry.
135 * As the index is only used during (de-) serialization, use this machinery
138 * \param[in] symbol Single StringTableEntry to obtain the index of.
139 * \returns Integer index to be used to obtain value from StringTable.
141 static int readIndexFromSerializer(const StringTableEntry
& symbol
)
143 gmx::InMemorySerializer writer
;
144 symbol
.serialize(&writer
);
145 auto buffer
= writer
.finishAndGetBuffer();
146 gmx::InMemoryDeserializer
reader(buffer
, false);
148 reader
.doInt(&index
);
153 * Helper function to check that a string in matches when looked up in non finalized table.
155 * Checks that a string looked up by using the index in the symbol table matches
156 * the string stored in the wrapper object obtained by entering a string.
158 * \param[in] symtab Symbol table that contains the entries.
159 * \param[in] symbol The entry obtained from placing a string in the symbol table.
160 * \param[in] string The string the entry should match.
162 static void stringMatches(const StringTable
& symtab
, const StringTableEntry
& symbol
, const char* string
)
164 int index
= readIndexFromSerializer(symbol
);
165 auto entryFromIndex
= symtab
.at(index
);
167 EXPECT_EQ(*entryFromIndex
, string
)
168 << "Index is " << index
<< " Entry from index is " << entryFromIndex
->c_str();
172 TEST_F(StringTableTest
, AddSingleEntry
)
174 builder().addString("foo");
175 StringTable table
= builder().build();
179 TEST_F(StringTableTest
, CanAccessWithAt
)
181 builder().addString("foo");
182 StringTable table
= builder().build();
183 EXPECT_NO_THROW(table
.at(0));
187 TEST_F(StringTableTest
, CanAccessWithBracket
)
189 builder().addString("foo");
190 StringTable table
= builder().build();
192 auto entry
= table
[0];
193 EXPECT_EQ(*entry
, "foo");
196 TEST_F(StringTableTest
, ThrowsOutOfRange
)
198 builder().addString("foo");
199 StringTable table
= builder().build();
200 EXPECT_THROW(table
.at(1), InternalError
);
204 TEST_F(StringTableTest
, StringCompareIsCorrect
)
206 auto fooSymbol
= builder().addString("foo");
207 StringTable table
= builder().build();
208 stringMatches(table
, fooSymbol
, "foo");
212 TEST_F(StringTableTest
, AddTwoDistinctEntries
)
214 auto fooSymbol
= builder().addString("foo");
215 auto barSymbol
= builder().addString("Bar");
217 EXPECT_FALSE(fooSymbol
== barSymbol
);
218 compareDifferentIndices(builder(), fooSymbol
, barSymbol
, false);
219 EXPECT_TRUE("foo" == *fooSymbol
);
220 EXPECT_TRUE("Bar" == *barSymbol
);
221 auto table
= builder().build();
222 stringMatches(table
, fooSymbol
, "foo");
223 stringMatches(table
, barSymbol
, "Bar");
227 TEST_F(StringTableTest
, TryToAddDuplicates
)
229 auto fooSymbol
= builder().addString("foo");
230 auto barSymbol
= builder().addString("Bar");
232 EXPECT_FALSE(fooSymbol
== barSymbol
);
233 EXPECT_FALSE(fooSymbol
->empty());
234 compareDifferentIndices(builder(), fooSymbol
, barSymbol
, false);
235 EXPECT_TRUE("foo" == *fooSymbol
);
236 EXPECT_TRUE("Bar" == *barSymbol
);
238 // Insert a duplicate element
239 auto anotherFooSymbol
= builder().addString("foo");
240 // Insert element with different case
241 auto capitalFooSymbol
= builder().addString("Foo");
243 // Check that no duplicate is made
244 EXPECT_TRUE(fooSymbol
== anotherFooSymbol
);
245 // Check case sensitivity
246 EXPECT_FALSE(fooSymbol
== capitalFooSymbol
);
248 // Check that underlying representation is same
249 EXPECT_TRUE("foo" == *anotherFooSymbol
);
250 EXPECT_TRUE("foo" == *fooSymbol
);
251 EXPECT_FALSE(*fooSymbol
== *capitalFooSymbol
);
252 EXPECT_TRUE("Bar" == *barSymbol
);
254 // Check for correct behaviours with new and old symbols
255 compareDifferentIndices(builder(), fooSymbol
, anotherFooSymbol
, true);
256 compareDifferentIndices(builder(), barSymbol
, anotherFooSymbol
, false);
257 compareDifferentIndices(builder(), fooSymbol
, barSymbol
, false);
258 compareDifferentIndices(builder(), fooSymbol
, capitalFooSymbol
, false);
259 auto table
= builder().build();
263 TEST_F(StringTableTest
, AddLargeNumberOfEntries
)
265 int numStringsToAdd
= 7; // Random number of strings.
266 std::vector
<StringTableEntry
> symbolsAdded
;
267 symbolsAdded
.reserve(numStringsToAdd
);
268 for (int i
= 0; i
< numStringsToAdd
; ++i
)
270 symbolsAdded
.push_back(builder().addString(toString(i
)));
272 for (int i
= 0; i
< numStringsToAdd
; ++i
)
274 EXPECT_TRUE(toString(i
) == *symbolsAdded
[i
]) << "index is " << i
;
276 // Add something unrelated and check that indices still work afterward.
277 builder().addString("foobar");
278 for (int i
= 0; i
< numStringsToAdd
; ++i
)
280 EXPECT_TRUE(toString(i
) == *symbolsAdded
[i
]) << "index is " << i
;
282 auto table
= builder().build();
283 for (int i
= 0; i
< numStringsToAdd
; ++i
)
285 stringMatches(table
, symbolsAdded
[i
], toString(i
).c_str());
290 TEST_F(StringTableTest
, NoDuplicatesInLargeTable
)
292 int halfOfStringsToAdd
= 7; // Random number of strings.
293 int totalNumStringsToAdd
= 2 * halfOfStringsToAdd
;
294 std::vector
<StringTableEntry
> symbolsAdded
;
295 symbolsAdded
.reserve(halfOfStringsToAdd
);
296 for (int i
= 0; i
< halfOfStringsToAdd
; ++i
)
298 symbolsAdded
.push_back(builder().addString(toString(i
)));
301 // We now try to mess around in the symtab.
302 auto bazSymbol
= builder().addString("baz");
304 // Now try to add more symbols, also including those that are already there.
305 for (int i
= 0; i
< totalNumStringsToAdd
; i
++)
307 symbolsAdded
.push_back(builder().addString(toString(i
)));
310 //! Check that entries that should be equal are, and new ones are not.
311 for (int i
= 0; i
< halfOfStringsToAdd
; i
++)
313 compareDifferentIndices(builder(), symbolsAdded
[i
], symbolsAdded
[halfOfStringsToAdd
+ i
], true);
314 compareDifferentIndices(builder(), symbolsAdded
[i
], symbolsAdded
[2 * halfOfStringsToAdd
+ i
], false);
315 compareDifferentIndices(builder(), symbolsAdded
[i
], bazSymbol
, false);
317 EXPECT_TRUE("baz" == *bazSymbol
);
318 symbolsAdded
.emplace_back(bazSymbol
);
319 auto table
= builder().build();
324 TEST_F(StringTableTest
, CanWriteToBuffer
)
326 builder().addString("foo");
327 builder().addString("bar");
328 builder().addString("baz");
329 auto finalTable
= builder().build();
330 InMemorySerializer writer
;
331 finalTable
.serializeStringTable(&writer
);
333 auto buffer
= writer
.finishAndGetBuffer();
334 EXPECT_EQ(buffer
.size(), 37); // 4 (size) + 3*(8 (string size) + 3*1 (char size) )
337 TEST_F(StringTableTest
, Roundtrip
)
339 // First generate a buffer from a string table
340 builder().addString("foo");
341 builder().addString("bar");
342 builder().addString("baz");
343 auto finalTable
= builder().build();
344 InMemorySerializer writer
;
345 finalTable
.serializeStringTable(&writer
);
347 auto buffer
= writer
.finishAndGetBuffer();
348 EXPECT_EQ(buffer
.size(), 37); // 4 (size) + 3*(8 (string size) + 3*1 (char size) )
350 // Now try to make a new table from it.
351 InMemoryDeserializer
reader(buffer
, false);
352 StringTable
readInTable(&reader
);
353 EXPECT_EQ(*(finalTable
.at(0)), *(readInTable
.at(0)));
354 EXPECT_EQ(*(finalTable
.at(1)), *(readInTable
.at(1)));
355 EXPECT_EQ(*(finalTable
.at(2)), *(readInTable
.at(2)));
358 TEST_F(StringTableTest
, RoundtripWithCorrectStringIndices
)
360 std::vector
<StringTableEntry
> testEntries
;
361 // First generate a buffer from a string table
362 testEntries
.emplace_back(builder().addString("foo"));
363 testEntries
.emplace_back(builder().addString("bar"));
364 testEntries
.emplace_back(builder().addString("baz"));
365 auto finalTable
= builder().build();
366 InMemorySerializer writer
;
367 finalTable
.serializeStringTable(&writer
);
368 for (const auto& stringEntry
: testEntries
)
370 stringEntry
.serialize(&writer
);
373 auto buffer
= writer
.finishAndGetBuffer();
374 EXPECT_EQ(buffer
.size(), 49); // 4 (size) + 3*(8 (string size) + 3*1 (char size) + 3*4 (int size))
376 // Now try to make a new table from it.
377 InMemoryDeserializer
reader(buffer
, false);
378 StringTable
readInTable(&reader
);
379 std::vector
<StringTableEntry
> deserializedEntries
;
380 for (index gmx_unused i
= 0; i
< gmx::ssize(testEntries
); i
++)
382 deserializedEntries
.emplace_back(readStringTableEntry(&reader
, readInTable
));
384 EXPECT_EQ(*(finalTable
.at(0)), *(deserializedEntries
[0]));
385 EXPECT_EQ(*(finalTable
.at(1)), *(deserializedEntries
[1]));
386 EXPECT_EQ(*(finalTable
.at(2)), *(deserializedEntries
[2]));
389 TEST_F(StringTableTest
, CanCopyToLegacyTable
)
391 auto fooSymbol
= builder().addString("foo");
392 auto barSymbol
= builder().addString("Bar");
394 StringTable finalTable
= builder().build();
396 t_symtab legacySymtab
;
397 open_symtab(&legacySymtab
);
398 finalTable
.copyToLegacySymtab(&legacySymtab
);
399 int fooEntryIndex
= readIndexFromSerializer(fooSymbol
);
400 int barEntryIndex
= readIndexFromSerializer(barSymbol
);
401 EXPECT_STREQ(finalTable
.at(fooEntryIndex
)->c_str(), *get_symtab_handle(&legacySymtab
, fooEntryIndex
));
402 EXPECT_STREQ(finalTable
.at(barEntryIndex
)->c_str(), *get_symtab_handle(&legacySymtab
, barEntryIndex
));
403 done_symtab(&legacySymtab
);
409 class LegacySymtabTest
: public ::testing::Test
412 LegacySymtabTest() { open_symtab(&symtab_
); }
413 ~LegacySymtabTest() override
415 done_symtab(&symtab_
);
416 EXPECT_EQ(symtab_
.nr
, 0);
417 EXPECT_EQ(symtab_
.symbuf
, nullptr);
420 //! Get handle to symbol table.
421 t_symtab
* symtab() { return &symtab_
; }
422 //! Dump symtab. Similar to pr_symtab function.
426 //! Get reference checker using lazy initialization
427 TestReferenceChecker
* checker()
431 checker_
= std::make_unique
<TestReferenceChecker
>(data_
.rootChecker());
433 return checker_
.get();
435 //! The symbol table being tested.
437 //! Handler for reference data.
438 TestReferenceData data_
;
439 //! Handler for checking reference data.
440 std::unique_ptr
<TestReferenceChecker
> checker_
;
443 void LegacySymtabTest::dumpSymtab()
446 t_symbuf
* symbuf
= symtab_
.symbuf
;
447 std::vector
<std::string
> symtabDump
;
449 while (symbuf
!= nullptr)
452 for (i
= 0; (i
< symbuf
->bufsize
) && (i
< nr
); i
++)
454 symtabDump
.emplace_back(formatString("Symtab[%d]=\"%s\"", pos
++, symbuf
->buf
[i
]));
457 symbuf
= symbuf
->next
;
459 checker()->checkSequence(symtabDump
.begin(), symtabDump
.end(), "Complete dump of SymbolTable");
463 * Helper that compares an input to a handle obtained from symtab lookup.
465 * \param[in] symtab Symbol table that contains the entries.
466 * \param[in] symbol The entry obtained from placing a string in the symbol table.
467 * \param[in] index Index into symtab corresponding to an entry.
468 * \returns Whether to \p symbol and the entry returned by \p index are the same pointer.
470 bool entriesAreEqual(t_symtab
* symtab
, char** symbol
, int index
)
472 return symbol
== get_symtab_handle(symtab
, index
);
476 * Helper function to check internal consistency of symtab lookup.
478 * Checks that placing an entry resulted in valid symbol table, and that
479 * the index obtained from a call to lookup_symtab returns the correct entry.
481 * \param[in] symtab Symbol table that contains the entries.
482 * \param[in] symbol The entry obtained from placing a string in the symbol table.
484 void compareSymtabLookupAndHandle(t_symtab
* symtab
, char** symbol
)
486 ASSERT_NE(symtab
->symbuf
, nullptr);
487 auto index
= lookup_symtab(symtab
, symbol
);
488 EXPECT_TRUE(entriesAreEqual(symtab
, symbol
, index
));
491 * Check that symbols obtained from symtab compare correctly.
493 * Helper function to find out if two entries obtained by a symtab lookup
494 * are equivalent or not, according to testing criteria.
496 * \param[in] symtab Symbol table that contains the entries.
497 * \param[in] firstSymbol Handle into symtab obtained from placing string in symtab.
498 * \param[in] otherSymbol Other handle from obtained from separate string deposit.
499 * \param[in] expectedOutcome If the handles should result in equal entries or not.
501 void compareDifferentHandles(t_symtab
* symtab
, char** firstSymbol
, char** otherSymbol
, bool expectedOutcome
)
503 ASSERT_NE(symtab
->symbuf
, nullptr);
504 auto firstIndex
= lookup_symtab(symtab
, firstSymbol
);
505 auto otherIndex
= lookup_symtab(symtab
, otherSymbol
);
506 EXPECT_EQ(expectedOutcome
, entriesAreEqual(symtab
, firstSymbol
, otherIndex
));
507 EXPECT_EQ(expectedOutcome
, entriesAreEqual(symtab
, otherSymbol
, firstIndex
));
510 TEST_F(LegacySymtabTest
, EmptyOnOpen
)
512 ASSERT_EQ(0, symtab()->nr
);
513 ASSERT_EQ(nullptr, symtab()->symbuf
);
516 TEST_F(LegacySymtabTest
, AddSingleEntry
)
518 auto fooSymbol
= put_symtab(symtab(), "Foo");
519 ASSERT_EQ(1, symtab()->nr
);
520 compareSymtabLookupAndHandle(symtab(), fooSymbol
);
521 EXPECT_STREQ("Foo", *fooSymbol
);
524 TEST_F(LegacySymtabTest
, AddTwoDistinctEntries
)
526 auto fooSymbol
= put_symtab(symtab(), "Foo");
527 auto barSymbol
= put_symtab(symtab(), "Bar");
528 ASSERT_EQ(2, symtab()->nr
);
530 compareSymtabLookupAndHandle(symtab(), fooSymbol
);
531 compareSymtabLookupAndHandle(symtab(), barSymbol
);
533 EXPECT_NE(fooSymbol
, barSymbol
);
534 compareDifferentHandles(symtab(), fooSymbol
, barSymbol
, false);
535 EXPECT_STREQ("Foo", *fooSymbol
);
536 EXPECT_STREQ("Bar", *barSymbol
);
539 TEST_F(LegacySymtabTest
, TryToAddDuplicates
)
541 auto fooSymbol
= put_symtab(symtab(), "Foo");
542 auto barSymbol
= put_symtab(symtab(), "Bar");
543 ASSERT_EQ(2, symtab()->nr
);
545 compareSymtabLookupAndHandle(symtab(), fooSymbol
);
546 compareSymtabLookupAndHandle(symtab(), barSymbol
);
548 EXPECT_NE(fooSymbol
, barSymbol
);
549 compareDifferentHandles(symtab(), fooSymbol
, barSymbol
, false);
550 EXPECT_STREQ("Foo", *fooSymbol
);
551 EXPECT_STREQ("Bar", *barSymbol
);
553 // Insert a duplicate element
554 auto anotherFooSymbol
= put_symtab(symtab(), "Foo");
555 ASSERT_EQ(2, symtab()->nr
);
557 // Check for correct post-conditions
558 EXPECT_EQ(fooSymbol
, anotherFooSymbol
);
559 EXPECT_STREQ("Foo", *anotherFooSymbol
);
560 EXPECT_STREQ("Foo", *fooSymbol
);
561 EXPECT_STREQ("Bar", *barSymbol
);
563 // Check for correct behaviours with new and old symbols
564 compareDifferentHandles(symtab(), fooSymbol
, anotherFooSymbol
, true);
565 compareDifferentHandles(symtab(), barSymbol
, anotherFooSymbol
, false);
566 compareDifferentHandles(symtab(), fooSymbol
, barSymbol
, false);
569 TEST_F(LegacySymtabTest
, AddLargeNumberOfEntries
)
571 int numStringsToAdd
= 7; // Larger than c_maxBufSize limit for size of symbuf.
572 std::vector
<char**> symbolsAdded
;
573 symbolsAdded
.reserve(numStringsToAdd
);
574 for (int i
= 0; i
< numStringsToAdd
; ++i
)
576 symbolsAdded
.push_back(put_symtab(symtab(), toString(i
).c_str()));
578 ASSERT_EQ(numStringsToAdd
, symtab()->nr
);
579 for (int i
= 0; i
< numStringsToAdd
; ++i
)
581 EXPECT_STREQ(toString(i
).c_str(), *symbolsAdded
[i
]);
582 compareSymtabLookupAndHandle(symtab(), symbolsAdded
[i
]);
584 // Add something unrelated and check that indices still work afterward.
585 auto foobarSymbol
= put_symtab(symtab(), "foobar");
586 ASSERT_EQ(numStringsToAdd
+ 1, symtab()->nr
);
587 for (int i
= 0; i
< numStringsToAdd
; ++i
)
589 EXPECT_STREQ(toString(i
).c_str(), *symbolsAdded
[i
]);
590 compareSymtabLookupAndHandle(symtab(), symbolsAdded
[i
]);
592 compareSymtabLookupAndHandle(symtab(), foobarSymbol
);
594 // Now dump the symtab to see that we can reproduce it if needed.
598 TEST_F(LegacySymtabTest
, NoDuplicatesInLargeTable
)
600 int halfOfStringsToAdd
= 7; // Larger than c_maxBufSize limit for size of symbuf.
601 int totalNumStringsToAdd
= 2 * halfOfStringsToAdd
;
602 std::vector
<char**> symbolsAdded
;
603 symbolsAdded
.reserve(halfOfStringsToAdd
);
604 for (int i
= 0; i
< halfOfStringsToAdd
; ++i
)
606 symbolsAdded
.push_back(put_symtab(symtab(), toString(i
).c_str()));
608 ASSERT_EQ(halfOfStringsToAdd
, symtab()->nr
);
610 // We now try to mess around in the symtab.
611 auto bazSymbol
= put_symtab(symtab(), "baz");
612 ASSERT_EQ(halfOfStringsToAdd
+ 1, symtab()->nr
);
613 compareSymtabLookupAndHandle(symtab(), bazSymbol
);
615 // Now try to add more symbols, also including those that are already there.
616 for (int i
= 0; i
< totalNumStringsToAdd
; i
++)
618 symbolsAdded
.push_back(put_symtab(symtab(), toString(i
).c_str()));
620 ASSERT_EQ(totalNumStringsToAdd
+ 1, symtab()->nr
);
622 //! Check that entries that should be equal are, and new ones are not.
623 for (int i
= 0; i
< halfOfStringsToAdd
; i
++)
625 compareDifferentHandles(symtab(), symbolsAdded
[i
], symbolsAdded
[halfOfStringsToAdd
+ i
], true);
626 compareDifferentHandles(symtab(), symbolsAdded
[i
], symbolsAdded
[2 * halfOfStringsToAdd
+ i
], false);
627 compareDifferentHandles(symtab(), symbolsAdded
[i
], bazSymbol
, false);
629 compareSymtabLookupAndHandle(symtab(), bazSymbol
);
630 EXPECT_STREQ("baz", *bazSymbol
);