From 896ddd992a0c700a9223bc78e1d63268fd08d6c6 Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Fri, 6 Nov 2020 01:35:36 +0000 Subject: [PATCH] Bug 1674968 - UniqueJSONStrings unit tests - r=gregtatum `UniqueJSONStrings` constructors now accept a `JSONWriter::CollectionStyle` to pass to the internal JSON writer. The default won't change how the unique string list is output (with friendly indented lines), but tests can use `JSONWriter::SingleLineStyle` to make comparison strings more readable and maintainable. Differential Revision: https://phabricator.services.mozilla.com/D95832 --- .../baseprofiler/core/ProfileJSONWriter.cpp | 9 +- .../public/BaseProfileJSONWriter.h | 7 +- mozglue/tests/TestBaseProfiler.cpp | 191 ++++++++++++++++++ 3 files changed, 202 insertions(+), 5 deletions(-) diff --git a/mozglue/baseprofiler/core/ProfileJSONWriter.cpp b/mozglue/baseprofiler/core/ProfileJSONWriter.cpp index 43d80c878738..7acbf49f7902 100644 --- a/mozglue/baseprofiler/core/ProfileJSONWriter.cpp +++ b/mozglue/baseprofiler/core/ProfileJSONWriter.cpp @@ -8,10 +8,13 @@ namespace mozilla::baseprofiler { -UniqueJSONStrings::UniqueJSONStrings() { mStringTableWriter.StartBareList(); } +UniqueJSONStrings::UniqueJSONStrings(JSONWriter::CollectionStyle aStyle) { + mStringTableWriter.StartBareList(aStyle); +} -UniqueJSONStrings::UniqueJSONStrings(const UniqueJSONStrings& aOther) { - mStringTableWriter.StartBareList(); +UniqueJSONStrings::UniqueJSONStrings(const UniqueJSONStrings& aOther, + JSONWriter::CollectionStyle aStyle) { + mStringTableWriter.StartBareList(aStyle); uint32_t count = mStringHashToIndexMap.count(); if (count != 0) { MOZ_RELEASE_ASSERT(mStringHashToIndexMap.reserve(count)); diff --git a/mozglue/baseprofiler/public/BaseProfileJSONWriter.h b/mozglue/baseprofiler/public/BaseProfileJSONWriter.h index cac14b8ff887..8c21a09c68d8 100644 --- a/mozglue/baseprofiler/public/BaseProfileJSONWriter.h +++ b/mozglue/baseprofiler/public/BaseProfileJSONWriter.h @@ -297,10 +297,13 @@ class JSONSchemaWriter { class UniqueJSONStrings { public: // Start an empty list of unique strings. - MFBT_API UniqueJSONStrings(); + MFBT_API explicit UniqueJSONStrings( + JSONWriter::CollectionStyle aStyle = JSONWriter::MultiLineStyle); // Start with a copy of the strings from another list. - MFBT_API explicit UniqueJSONStrings(const UniqueJSONStrings& aOther); + MFBT_API explicit UniqueJSONStrings( + const UniqueJSONStrings& aOther, + JSONWriter::CollectionStyle aStyle = JSONWriter::MultiLineStyle); MFBT_API ~UniqueJSONStrings(); diff --git a/mozglue/tests/TestBaseProfiler.cpp b/mozglue/tests/TestBaseProfiler.cpp index a6e1d9b794fa..4fdea0ae1c3c 100644 --- a/mozglue/tests/TestBaseProfiler.cpp +++ b/mozglue/tests/TestBaseProfiler.cpp @@ -3555,6 +3555,196 @@ void TestProfiler() { printf("TestProfiler done\n"); } +// Minimal string escaping, similar to how C++ stringliterals should be entered, +// to help update comparison strings in tests below. +void printEscaped(std::string_view aString) { + for (const char c : aString) { + switch (c) { + case '\n': + fprintf(stderr, "\\n\n"); + break; + case '"': + fprintf(stderr, "\\\""); + break; + case '\\': + fprintf(stderr, "\\\\"); + break; + default: + if (c >= ' ' && c <= '~') { + fprintf(stderr, "%c", c); + } else { + fprintf(stderr, "\\x%02x", unsigned(c)); + } + break; + } + } +} + +// Run aF(SpliceableChunkedJSONWriter&, UniqueJSONStrings&) from inside a JSON +// array, then output the string table, and compare the full output to +// aExpected. +template +static void VerifyUniqueStringContents( + F&& aF, std::string_view aExpectedData, + std::string_view aExpectedUniqueStrings, + mozilla::baseprofiler::UniqueJSONStrings* aUniqueStringsOrNull = nullptr) { + mozilla::baseprofiler::SpliceableChunkedJSONWriter writer; + + // By default use a local UniqueJSONStrings, otherwise use the one provided. + mozilla::baseprofiler::UniqueJSONStrings localUniqueStrings( + mozilla::JSONWriter::SingleLineStyle); + mozilla::baseprofiler::UniqueJSONStrings& uniqueStrings = + aUniqueStringsOrNull ? *aUniqueStringsOrNull : localUniqueStrings; + + writer.Start(mozilla::JSONWriter::SingleLineStyle); + { + writer.StartArrayProperty("data", mozilla::JSONWriter::SingleLineStyle); + { std::forward(aF)(writer, uniqueStrings); } + writer.EndArray(); + + writer.StartArrayProperty("stringTable", + mozilla::JSONWriter::SingleLineStyle); + { uniqueStrings.SpliceStringTableElements(writer); } + writer.EndArray(); + } + writer.End(); + + UniquePtr jsonString = writer.ChunkedWriteFunc().CopyData(); + MOZ_RELEASE_ASSERT(jsonString); + std::string_view jsonStringView(jsonString.get()); + std::string expected = "{\"data\": ["; + expected += aExpectedData; + expected += "], \"stringTable\": ["; + expected += aExpectedUniqueStrings; + expected += "]}\n"; + if (jsonStringView != expected) { + fprintf(stderr, + "Expected:\n" + "------\n"); + printEscaped(expected); + fprintf(stderr, + "\n" + "------\n" + "Actual:\n" + "------\n"); + printEscaped(jsonStringView); + fprintf(stderr, + "\n" + "------\n"); + } + MOZ_RELEASE_ASSERT(jsonStringView == expected); +} + +void TestUniqueJSONStrings() { + printf("TestUniqueJSONStrings...\n"); + + using SCJW = mozilla::baseprofiler::SpliceableChunkedJSONWriter; + using UJS = mozilla::baseprofiler::UniqueJSONStrings; + + // Empty everything. + VerifyUniqueStringContents([](SCJW& aWriter, UJS& aUniqueStrings) {}, "", ""); + + // Empty unique strings. + VerifyUniqueStringContents( + [](SCJW& aWriter, UJS& aUniqueStrings) { + aWriter.StringElement("string"); + }, + R"("string")", ""); + + // One unique string. + VerifyUniqueStringContents( + [](SCJW& aWriter, UJS& aUniqueStrings) { + aUniqueStrings.WriteElement(aWriter, "string"); + }, + "0", R"("string")"); + + // One unique string twice. + VerifyUniqueStringContents( + [](SCJW& aWriter, UJS& aUniqueStrings) { + aUniqueStrings.WriteElement(aWriter, "string"); + aUniqueStrings.WriteElement(aWriter, "string"); + }, + "0, 0", R"("string")"); + + // Two single unique strings. + VerifyUniqueStringContents( + [](SCJW& aWriter, UJS& aUniqueStrings) { + aUniqueStrings.WriteElement(aWriter, "string0"); + aUniqueStrings.WriteElement(aWriter, "string1"); + }, + "0, 1", R"("string0", "string1")"); + + // Two unique strings with repetition. + VerifyUniqueStringContents( + [](SCJW& aWriter, UJS& aUniqueStrings) { + aUniqueStrings.WriteElement(aWriter, "string0"); + aUniqueStrings.WriteElement(aWriter, "string1"); + aUniqueStrings.WriteElement(aWriter, "string0"); + }, + "0, 1, 0", R"("string0", "string1")"); + + // Mix some object properties, for coverage. + VerifyUniqueStringContents( + [](SCJW& aWriter, UJS& aUniqueStrings) { + aUniqueStrings.WriteElement(aWriter, "string0"); + aWriter.StartObjectElement(mozilla::JSONWriter::SingleLineStyle); + { + aUniqueStrings.WriteProperty(aWriter, "p0", "prop"); + aUniqueStrings.WriteProperty(aWriter, "p1", "string0"); + aUniqueStrings.WriteProperty(aWriter, "p2", "prop"); + } + aWriter.EndObject(); + aUniqueStrings.WriteElement(aWriter, "string1"); + aUniqueStrings.WriteElement(aWriter, "string0"); + aUniqueStrings.WriteElement(aWriter, "prop"); + }, + R"(0, {"p0": 1, "p1": 0, "p2": 1}, 2, 0, 1)", + R"("string0", "prop", "string1")"); + + // Unique string table with pre-existing data. + { + UJS ujs(mozilla::JSONWriter::SingleLineStyle); + { + SCJW writer; + ujs.WriteElement(writer, "external0"); + ujs.WriteElement(writer, "external1"); + ujs.WriteElement(writer, "external0"); + } + VerifyUniqueStringContents( + [](SCJW& aWriter, UJS& aUniqueStrings) { + aUniqueStrings.WriteElement(aWriter, "string0"); + aUniqueStrings.WriteElement(aWriter, "string1"); + aUniqueStrings.WriteElement(aWriter, "string0"); + }, + "2, 3, 2", R"("external0", "external1", "string0", "string1")", &ujs); + } + +// This currently fails, demonstrating the regression from bug 1520104, to be +// fixed in the next patch in bug 1674968. +# if 0 + // Unique string table with pre-existing data from another table. + { + UJS ujs(mozilla::JSONWriter::SingleLineStyle); + { + SCJW writer; + ujs.WriteElement(writer, "external0"); + ujs.WriteElement(writer, "external1"); + ujs.WriteElement(writer, "external0"); + } + UJS ujsCopy(ujs, mozilla::JSONWriter::SingleLineStyle); + VerifyUniqueStringContents( + [](SCJW& aWriter, UJS& aUniqueStrings) { + aUniqueStrings.WriteElement(aWriter, "string0"); + aUniqueStrings.WriteElement(aWriter, "string1"); + aUniqueStrings.WriteElement(aWriter, "string0"); + }, + "2, 3, 2", R"("external0", "external1", "string0", "string1")", &ujs); + } +# endif + + printf("TestUniqueJSONStrings done\n"); +} + void StreamMarkers(const mozilla::ProfileChunkedBuffer& aBuffer, mozilla::JSONWriter& aWriter) { aWriter.Start(); @@ -3872,6 +4062,7 @@ void TestProfilerMarkers() { mozilla::baseprofiler::profiler_current_thread_id()); // ::SleepMilli(10000); + TestUniqueJSONStrings(); TestMarkerCategory(); TestMarkerThreadId(); TestMarkerNoPayload();