diff options
Diffstat (limited to 'dom/base/test/gtest')
-rw-r--r-- | dom/base/test/gtest/TestContentUtils.cpp | 102 | ||||
-rw-r--r-- | dom/base/test/gtest/TestMimeType.cpp | 816 | ||||
-rw-r--r-- | dom/base/test/gtest/TestParser.cpp | 52 | ||||
-rw-r--r-- | dom/base/test/gtest/TestPlainTextSerializer.cpp | 258 | ||||
-rw-r--r-- | dom/base/test/gtest/TestScheduler.cpp | 341 | ||||
-rw-r--r-- | dom/base/test/gtest/TestXPathGenerator.cpp | 150 | ||||
-rw-r--r-- | dom/base/test/gtest/moz.build | 20 |
7 files changed, 1739 insertions, 0 deletions
diff --git a/dom/base/test/gtest/TestContentUtils.cpp b/dom/base/test/gtest/TestContentUtils.cpp new file mode 100644 index 0000000000..8acc15a5cc --- /dev/null +++ b/dom/base/test/gtest/TestContentUtils.cpp @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" + +#include "jsapi.h" +#include "nsContentUtils.h" +#include "nsNetUtil.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/SimpleGlobalObject.h" + +struct IsURIInListMatch { + nsLiteralCString pattern; + bool firstMatch, secondMatch; +}; + +TEST(DOM_Base_ContentUtils, IsURIInList) +{ + nsCOMPtr<nsIURI> uri, subURI; + nsresult rv = NS_NewURI(getter_AddRefs(uri), + "https://example.com/path/favicon.ico#"_ns); + ASSERT_TRUE(rv == NS_OK); + + rv = NS_NewURI(getter_AddRefs(subURI), + "http://sub.example.com/favicon.ico?"_ns); + ASSERT_TRUE(rv == NS_OK); + + static constexpr IsURIInListMatch patterns[] = { + {"bar.com,*.example.com,example.com,foo.com"_ns, true, true}, + {"bar.com,example.com,*.example.com,foo.com"_ns, true, true}, + {"*.example.com,example.com,foo.com"_ns, true, true}, + {"example.com,*.example.com,foo.com"_ns, true, true}, + {"*.example.com,example.com"_ns, true, true}, + {"example.com,*.example.com"_ns, true, true}, + {"*.example.com/,example.com/"_ns, true, true}, + {"example.com/,*.example.com/"_ns, true, true}, + {"*.example.com/pa,example.com/pa"_ns, false, false}, + {"example.com/pa,*.example.com/pa"_ns, false, false}, + {"*.example.com/pa/,example.com/pa/"_ns, false, false}, + {"example.com/pa/,*.example.com/pa/"_ns, false, false}, + {"*.example.com/path,example.com/path"_ns, false, false}, + {"example.com/path,*.example.com/path"_ns, false, false}, + {"*.example.com/path/,example.com/path/"_ns, true, false}, + {"example.com/path/,*.example.com/path/"_ns, true, false}, + {"*.example.com/favicon.ico"_ns, false, true}, + {"example.com/path/favicon.ico"_ns, true, false}, + {"*.example.com"_ns, false, true}, + {"example.com"_ns, true, false}, + {"foo.com"_ns, false, false}, + {"*.foo.com"_ns, false, false}, + }; + + for (auto& entry : patterns) { + bool result = nsContentUtils::IsURIInList(uri, entry.pattern); + ASSERT_EQ(result, entry.firstMatch) << "Matching " << entry.pattern; + + result = nsContentUtils::IsURIInList(subURI, entry.pattern); + ASSERT_EQ(result, entry.secondMatch) << "Matching " << entry.pattern; + } +} + +TEST(DOM_Base_ContentUtils, StringifyJSON_EmptyValue) +{ + JS::RootedObject globalObject( + mozilla::dom::RootingCx(), + mozilla::dom::SimpleGlobalObject::Create( + mozilla::dom::SimpleGlobalObject::GlobalType::BindingDetail)); + mozilla::dom::AutoJSAPI jsAPI; + ASSERT_TRUE(jsAPI.Init(globalObject)); + JSContext* cx = jsAPI.cx(); + nsAutoString serializedValue; + + JS::RootedValue jsValue(cx); + ASSERT_TRUE(nsContentUtils::StringifyJSON(cx, &jsValue, serializedValue)); + + ASSERT_TRUE(serializedValue.EqualsLiteral("null")); +} + +TEST(DOM_Base_ContentUtils, StringifyJSON_Object) +{ + JS::RootedObject globalObject( + mozilla::dom::RootingCx(), + mozilla::dom::SimpleGlobalObject::Create( + mozilla::dom::SimpleGlobalObject::GlobalType::BindingDetail)); + mozilla::dom::AutoJSAPI jsAPI; + ASSERT_TRUE(jsAPI.Init(globalObject)); + JSContext* cx = jsAPI.cx(); + nsAutoString serializedValue; + + JS::RootedObject jsObj(cx, JS_NewPlainObject(cx)); + JS::RootedString valueStr(cx, JS_NewStringCopyZ(cx, "Hello World!")); + ASSERT_TRUE(JS_DefineProperty(cx, jsObj, "key1", valueStr, JSPROP_ENUMERATE)); + JS::RootedValue jsValue(cx, JS::ObjectValue(*jsObj)); + + ASSERT_TRUE(nsContentUtils::StringifyJSON(cx, &jsValue, serializedValue)); + + ASSERT_TRUE(serializedValue.EqualsLiteral("{\"key1\":\"Hello World!\"}")); +} diff --git a/dom/base/test/gtest/TestMimeType.cpp b/dom/base/test/gtest/TestMimeType.cpp new file mode 100644 index 0000000000..64a2b8f4bd --- /dev/null +++ b/dom/base/test/gtest/TestMimeType.cpp @@ -0,0 +1,816 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" + +#include "MimeType.h" +#include "nsString.h" + +using mozilla::UniquePtr; + +TEST(MimeType, EmptyString) +{ + const auto in = u""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Empty string"; +} + +TEST(MimeType, JustWhitespace) +{ + const auto in = u" \t\r\n "_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Just whitespace"; +} + +TEST(MimeType, JustBackslash) +{ + const auto in = u"\\"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Just backslash"; +} + +TEST(MimeType, JustForwardslash) +{ + const auto in = u"/"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Just forward slash"; +} + +TEST(MimeType, MissingType1) +{ + const auto in = u"/bogus"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Missing type #1"; +} + +TEST(MimeType, MissingType2) +{ + const auto in = u" \r\n\t/bogus"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Missing type #2"; +} + +TEST(MimeType, MissingSubtype1) +{ + const auto in = u"bogus"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Missing subtype #1"; +} + +TEST(MimeType, MissingSubType2) +{ + const auto in = u"bogus/"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Missing subtype #2"; +} + +TEST(MimeType, MissingSubType3) +{ + const auto in = u"bogus;"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Missing subtype #3"; +} + +TEST(MimeType, MissingSubType4) +{ + const auto in = u"bogus; \r\n\t"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Missing subtype #3"; +} + +TEST(MimeType, ExtraForwardSlash) +{ + const auto in = u"bogus/bogus/;"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Extra forward slash"; +} + +TEST(MimeType, WhitespaceInType) +{ + const auto in = u"t\re\nx\tt /html"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Type with whitespace"; +} + +TEST(MimeType, WhitespaceInSubtype) +{ + const auto in = u"text/ h\rt\nm\tl"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Subtype with whitespace"; +} + +TEST(MimeType, NonAlphanumericMediaType1) +{ + const auto in = u"</>"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Non-alphanumeric media type #1"; +} + +TEST(MimeType, NonAlphanumericMediaType2) +{ + const auto in = u"(/)"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Non-alphanumeric media type #2"; +} + +TEST(MimeType, NonAlphanumericMediaType3) +{ + const auto in = u"{/}"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Non-alphanumeric media type #3"; +} + +TEST(MimeType, NonAlphanumericMediaType4) +{ + const auto in = u"\"/\""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Non-alphanumeric media type #4"; +} + +TEST(MimeType, NonAlphanumericMediaType5) +{ + const auto in = u"\0/\0"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Non-alphanumeric media type #5"; +} + +TEST(MimeType, NonAlphanumericMediaType6) +{ + const auto in = u"text/html(;doesnot=matter"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Non-alphanumeric media type #6"; +} + +TEST(MimeType, NonLatin1MediaType1) +{ + const auto in = u"ÿ/ÿ"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Non-latin1 media type #1"; +} + +TEST(MimeType, NonLatin1MediaType2) +{ + const auto in = u"\x0100/\x0100"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_FALSE(parsed) + << "Non-latin1 media type #2"; +} + +TEST(MimeType, MultipleParameters) +{ + const auto in = u"text/html;charset=gbk;no=1;charset_=gbk_;yes=2"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsString out; + parsed->Serialize(out); + ASSERT_TRUE(out.Equals(u"text/html;charset=gbk;no=1;charset_=gbk_;yes=2"_ns)) + << "Multiple parameters"; +} + +TEST(MimeType, DuplicateParameter1) +{ + const auto in = u"text/html;charset=gbk;charset=windows-1255"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsString out; + parsed->Serialize(out); + ASSERT_TRUE(out.Equals(u"text/html;charset=gbk"_ns)) + << "Duplicate parameter #1"; +} + +TEST(MimeType, DuplicateParameter2) +{ + const auto in = u"text/html;charset=();charset=GBK"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsString out; + parsed->Serialize(out); + ASSERT_TRUE(out.Equals(u"text/html;charset=\"()\""_ns)) + << "Duplicate parameter #2"; +} + +TEST(MimeType, CString) +{ + const auto in = "text/html;charset=();charset=GBK"_ns; + UniquePtr<CMimeType> parsed = CMimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsCString out; + parsed->Serialize(out); + ASSERT_TRUE(out.Equals("text/html;charset=\"()\""_ns)) + << "Duplicate parameter #2"; +} + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4819) +#endif +TEST(MimeType, NonAlphanumericParametersAreQuoted) +{ + const auto in = u"text/html;test=\x00FF\\;charset=gbk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsString out; + parsed->Serialize(out); + ASSERT_TRUE(out.Equals(u"text/html;test=\"\x00FF\\\\\";charset=gbk"_ns)) + << "Non-alphanumeric parameters are quoted"; +} +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace1) +{ + const auto in = u"text/html;charset= g\\\"bk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" g\\\\\\\"bk\"")) + << "Parameter is quoted if has leading whitespace #1"; +} + +TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace2) +{ + const auto in = u"text/html;charset= \"g\\bk\""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" \\\"g\\\\bk\\\"\"")) + << "Parameter is quoted if has leading whitespace #2"; +} + +TEST(MimeType, ParameterQuotedIfHasInternalWhitespace) +{ + const auto in = u"text/html;charset=g \\b\"k"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"g \\\\b\\\"k\"")) + << "Parameter is quoted if has internal whitespace"; +} + +TEST(MimeType, ImproperlyQuotedParameter1) +{ + const auto in = u"x/x;test=\""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("x/x;test=\"\"")) + << "Improperly-quoted parameter is handled properly #1"; +} + +TEST(MimeType, ImproperlyQuotedParameter2) +{ + const auto in = u"x/x;test=\"\\"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("x/x;test=\"\\\\\"")) + << "Improperly-quoted parameter is handled properly #2"; +} + +TEST(MimeType, NonLatin1ParameterIgnored) +{ + const auto in = u"x/x;test=\xFFFD;x=x"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("x/x;x=x")) + << "Non latin-1 parameters are ignored"; +} + +TEST(MimeType, ParameterIgnoredIfWhitespaceInName1) +{ + const auto in = u"text/html;charset =gbk;charset=123"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=123")) + << "Parameter ignored if whitespace in name #1"; +} + +TEST(MimeType, ParameterIgnoredIfWhitespaceInName2) +{ + const auto in = u"text/html;cha rset =gbk;charset=123"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=123")) + << "Parameter ignored if whitespace in name #2"; +} + +TEST(MimeType, WhitespaceTrimmed) +{ + const auto in = u"\n\r\t text/plain\n\r\t ;\n\r\t charset=123\n\r\t "_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/plain;charset=123")) + << "Whitespace appropriately ignored"; +} + +TEST(MimeType, WhitespaceOnlyParameterIgnored) +{ + const auto in = u"x/x;x= \r\n\t"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("x/x")) + << "Whitespace-only parameter is ignored"; +} + +TEST(MimeType, IncompleteParameterIgnored1) +{ + const auto in = u"x/x;test"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("x/x")) + << "Incomplete parameter is ignored #1"; +} + +TEST(MimeType, IncompleteParameterIgnored2) +{ + const auto in = u"x/x;test="_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("x/x")) + << "Incomplete parameter is ignored #2"; +} + +TEST(MimeType, IncompleteParameterIgnored3) +{ + const auto in = u"x/x;test= \r\n\t"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("x/x")) + << "Incomplete parameter is ignored #3"; +} + +TEST(MimeType, IncompleteParameterIgnored4) +{ + const auto in = u"text/html;test;charset=gbk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Incomplete parameter is ignored #4"; +} + +TEST(MimeType, IncompleteParameterIgnored5) +{ + const auto in = u"text/html;test=;charset=gbk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Incomplete parameter is ignored #5"; +} + +TEST(MimeType, EmptyParameterIgnored1) +{ + const auto in = u"text/html ; ; charset=gbk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Empty parameter ignored #1"; +} + +TEST(MimeType, EmptyParameterIgnored2) +{ + const auto in = u"text/html;;;;charset=gbk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Empty parameter ignored #2"; +} + +TEST(MimeType, InvalidParameterIgnored1) +{ + const auto in = u"text/html;';charset=gbk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Invalid parameter ignored #1"; +} + +TEST(MimeType, InvalidParameterIgnored2) +{ + const auto in = u"text/html;\";charset=gbk;=123; =321"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Invalid parameter ignored #2"; +} + +TEST(MimeType, InvalidParameterIgnored3) +{ + const auto in = u"text/html;charset= \"\u007F;charset=GBK"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=GBK")) + << "Invalid parameter ignored #3"; +} + +TEST(MimeType, InvalidParameterIgnored4) +{ + const auto in = nsLiteralString( + u"text/html;charset=\"\u007F;charset=foo\";charset=GBK;charset="); + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=GBK")) + << "Invalid parameter ignored #4"; +} + +TEST(MimeType, SingleQuotes1) +{ + const auto in = u"text/html;charset='gbk'"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset='gbk'")) + << "Single quotes handled properly #1"; +} + +TEST(MimeType, SingleQuotes2) +{ + const auto in = u"text/html;charset='gbk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset='gbk")) + << "Single quotes handled properly #2"; +} + +TEST(MimeType, SingleQuotes3) +{ + const auto in = u"text/html;charset=gbk'"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk'")) + << "Single quotes handled properly #3"; +} + +TEST(MimeType, SingleQuotes4) +{ + const auto in = u"text/html;charset=';charset=GBK"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset='")) + << "Single quotes handled properly #4"; +} + +TEST(MimeType, SingleQuotes5) +{ + const auto in = u"text/html;charset=''';charset=GBK"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset='''")) + << "Single quotes handled properly #5"; +} + +TEST(MimeType, DoubleQuotes1) +{ + const auto in = u"text/html;charset=\"gbk\""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Double quotes handled properly #1"; +} + +TEST(MimeType, DoubleQuotes2) +{ + const auto in = u"text/html;charset=\"gbk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Double quotes handled properly #2"; +} + +TEST(MimeType, DoubleQuotes3) +{ + const auto in = u"text/html;charset=gbk\""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"gbk\\\"\"")) + << "Double quotes handled properly #3"; +} + +TEST(MimeType, DoubleQuotes4) +{ + const auto in = u"text/html;charset=\" gbk\""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" gbk\"")) + << "Double quotes handled properly #4"; +} + +TEST(MimeType, DoubleQuotes5) +{ + const auto in = u"text/html;charset=\"gbk \""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"gbk \"")) + << "Double quotes handled properly #5"; +} + +TEST(MimeType, DoubleQuotes6) +{ + const auto in = u"text/html;charset=\"\\ gbk\""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" gbk\"")) + << "Double quotes handled properly #6"; +} + +TEST(MimeType, DoubleQuotes7) +{ + const auto in = u"text/html;charset=\"\\g\\b\\k\""_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Double quotes handled properly #7"; +} + +TEST(MimeType, DoubleQuotes8) +{ + const auto in = u"text/html;charset=\"gbk\"x"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) + << "Double quotes handled properly #8"; +} + +TEST(MimeType, DoubleQuotes9) +{ + const auto in = u"text/html;charset=\"\";charset=GBK"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"\"")) + << "Double quotes handled properly #9"; +} + +TEST(MimeType, DoubleQuotes10) +{ + const auto in = u"text/html;charset=\";charset=GBK"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\";charset=GBK\"")) + << "Double quotes handled properly #10"; +} + +TEST(MimeType, UnexpectedCodePoints) +{ + const auto in = u"text/html;charset={gbk}"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"{gbk}\"")) + << "Unexpected code points handled properly"; +} + +TEST(MimeType, LongTypesSubtypesAccepted) +{ + const auto in = nsLiteralString( + u"01234567890123456789012345678901234567890123456789012345678901234567890" + u"1" + "2345678901234567890123456789012345678901234567890123456789/" + "012345678901234567890123456789012345678901234567890123456789012345678901" + "2345678901234567890123456789012345678901234567890123456789"); + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.Equals(in)) + << "Long type/subtype accepted"; +} + +TEST(MimeType, LongParametersAccepted) +{ + const auto in = nsLiteralString( + u"text/" + "html;" + "012345678901234567890123456789012345678901234567890123456789012345678901" + "2345678901234567890123456789012345678901234567890123456789=x;charset=" + "gbk"); + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.Equals(in)) + << "Long parameters accepted"; +} + +TEST(MimeType, AllValidCharactersAccepted1) +{ + const auto in = nsLiteralString( + u"x/x;x=\"\t " + u"!\\\"#$%&'()*+,-./" + u"0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`" + u"abcdefghijklmnopqrstuvwxyz{|}~" + u"\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A" + u"\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095" + u"\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F\u00A0" + u"\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB" + u"\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6" + u"\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0\u00C1" + u"\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC" + u"\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7" + u"\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2" + u"\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED" + u"\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8" + u"\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF\""); + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.Equals(in)) + << "All valid characters accepted #1"; +} + +TEST(MimeType, CaseNormalization1) +{ + const auto in = u"TEXT/PLAIN;CHARSET=TEST"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/plain;charset=TEST")) + << "Case normalized properly #1"; +} + +TEST(MimeType, CaseNormalization2) +{ + const auto in = nsLiteralString( + u"!#$%&'*+-.^_`|~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/" + "!#$%&'*+-.^_`|~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-" + ".^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$" + "%&'*+-.^_`|~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral( + "!#$%&'*+-.^_`|~" + "0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/" + "!#$%&'*+-.^_`|~" + "0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz;!#$%&'*+-" + ".^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz=!#$" + "%&'*+-.^_`|~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")) + << "Case normalized properly #2"; +} + +TEST(MimeType, LegacyCommentSyntax1) +{ + const auto in = u"text/html;charset=gbk("_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"gbk(\"")) + << "Legacy comment syntax #1"; +} + +TEST(MimeType, LegacyCommentSyntax2) +{ + const auto in = u"text/html;x=(;charset=gbk"_ns; + UniquePtr<MimeType> parsed = MimeType::Parse(in); + ASSERT_TRUE(parsed) + << "Parsing succeeded"; + nsAutoString out; + parsed->Serialize(out); + ASSERT_TRUE(out.EqualsLiteral("text/html;x=\"(\";charset=gbk")) + << "Legacy comment syntax #2"; +} diff --git a/dom/base/test/gtest/TestParser.cpp b/dom/base/test/gtest/TestParser.cpp new file mode 100644 index 0000000000..d9240cced7 --- /dev/null +++ b/dom/base/test/gtest/TestParser.cpp @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "mozilla/dom/DOMParser.h" +#include "mozilla/dom/Document.h" +#include "nsIDocumentEncoder.h" +#include "mozilla/ErrorResult.h" + +// This is a test for mozilla::dom::DOMParser::CreateWithoutGlobal() which was +// implemented for use in Thunderbird's MailNews module. + +// int main(int argc, char** argv) +TEST(TestParser, TestParserMain) +{ + bool allTestsPassed = false; + constexpr auto htmlInput = + u"<html><head>" + "<meta http-equiv=\"content-type\" content=\"text/html; charset=\">" + "</head><body>Hello <b>Thunderbird!</b></body></html>"_ns; + + do { + // Parse the HTML source. + mozilla::IgnoredErrorResult rv2; + RefPtr<mozilla::dom::DOMParser> parser = + mozilla::dom::DOMParser::CreateWithoutGlobal(rv2); + if (rv2.Failed()) break; + nsCOMPtr<mozilla::dom::Document> document = parser->ParseFromString( + htmlInput, mozilla::dom::SupportedType::Text_html, rv2); + if (rv2.Failed()) break; + + // Serialize it back to HTML source again. + nsCOMPtr<nsIDocumentEncoder> encoder = + do_createDocumentEncoder("text/html"); + if (!encoder) break; + nsresult rv = + encoder->Init(document, u"text/html"_ns, nsIDocumentEncoder::OutputRaw); + if (NS_FAILED(rv)) break; + nsString parsed; + rv = encoder->EncodeToString(parsed); + if (NS_FAILED(rv)) break; + + EXPECT_TRUE(parsed.Equals(htmlInput)); + allTestsPassed = true; + } while (false); + + EXPECT_TRUE(allTestsPassed); +} diff --git a/dom/base/test/gtest/TestPlainTextSerializer.cpp b/dom/base/test/gtest/TestPlainTextSerializer.cpp new file mode 100644 index 0000000000..ae60755abd --- /dev/null +++ b/dom/base/test/gtest/TestPlainTextSerializer.cpp @@ -0,0 +1,258 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" + +#include "nsServiceManagerUtils.h" +#include "nsString.h" +#include "nsIDocumentEncoder.h" +#include "nsCRT.h" +#include "nsIParserUtils.h" + +const uint32_t kDefaultWrapColumn = 72; + +void ConvertBufToPlainText(nsString& aConBuf, int aFlag, uint32_t aWrapColumn) { + nsCOMPtr<nsIParserUtils> utils = do_GetService(NS_PARSERUTILS_CONTRACTID); + utils->ConvertToPlainText(aConBuf, aFlag, aWrapColumn, aConBuf); +} + +// Test for ASCII with format=flowed; delsp=yes +TEST(PlainTextSerializer, ASCIIWithFlowedDelSp) +{ + nsString test; + nsString result; + + test.AssignLiteral( + "<html><body>" + "Firefox Firefox Firefox Firefox " + "Firefox Firefox Firefox Firefox " + "Firefox Firefox Firefox Firefox" + "</body></html>"); + + ConvertBufToPlainText(test, + nsIDocumentEncoder::OutputFormatted | + nsIDocumentEncoder::OutputCRLineBreak | + nsIDocumentEncoder::OutputLFLineBreak | + nsIDocumentEncoder::OutputFormatFlowed | + nsIDocumentEncoder::OutputFormatDelSp, + kDefaultWrapColumn); + + // create result case + result.AssignLiteral( + "Firefox Firefox Firefox Firefox " + "Firefox Firefox Firefox Firefox " + "Firefox \r\nFirefox Firefox Firefox\r\n"); + + ASSERT_TRUE(test.Equals(result)) + << "Wrong HTML to ASCII text serialization with format=flowed; delsp=yes"; +} + +// Test for CJK with format=flowed; delsp=yes +TEST(PlainTextSerializer, CJKWithFlowedDelSp) +{ + nsString test; + nsString result; + + test.AssignLiteral("<html><body>"); + for (uint32_t i = 0; i < 40; i++) { + // Insert Kanji (U+5341) + test.Append(0x5341); + } + test.AppendLiteral("</body></html>"); + + ConvertBufToPlainText(test, + nsIDocumentEncoder::OutputFormatted | + nsIDocumentEncoder::OutputCRLineBreak | + nsIDocumentEncoder::OutputLFLineBreak | + nsIDocumentEncoder::OutputFormatFlowed | + nsIDocumentEncoder::OutputFormatDelSp, + kDefaultWrapColumn); + + // create result case + for (uint32_t i = 0; i < 36; i++) { + result.Append(0x5341); + } + result.AppendLiteral(" \r\n"); + for (uint32_t i = 0; i < 4; i++) { + result.Append(0x5341); + } + result.AppendLiteral("\r\n"); + + ASSERT_TRUE(test.Equals(result)) + << "Wrong HTML to CJK text serialization with format=flowed; delsp=yes"; +} + +// Test for CJK with DisallowLineBreaking +TEST(PlainTextSerializer, CJKWithDisallowLineBreaking) +{ + nsString test; + nsString result; + + test.AssignLiteral("<html><body>"); + for (uint32_t i = 0; i < 400; i++) { + // Insert Kanji (U+5341) + test.Append(0x5341); + } + test.AppendLiteral("</body></html>"); + + ConvertBufToPlainText(test, + nsIDocumentEncoder::OutputFormatted | + nsIDocumentEncoder::OutputCRLineBreak | + nsIDocumentEncoder::OutputLFLineBreak | + nsIDocumentEncoder::OutputFormatFlowed | + nsIDocumentEncoder::OutputDisallowLineBreaking, + kDefaultWrapColumn); + + // create result case + for (uint32_t i = 0; i < 400; i++) { + result.Append(0x5341); + } + result.AppendLiteral("\r\n"); + + ASSERT_TRUE(test.Equals(result)) + << "Wrong HTML to CJK text serialization with OutputDisallowLineBreaking"; +} + +// Test for ASCII with format=flowed; and quoted lines in preformatted span. +TEST(PlainTextSerializer, PreformatFlowedQuotes) +{ + nsString test; + nsString result; + + test.AssignLiteral( + "<html><body>" + "<span style=\"white-space: pre-wrap;\" _moz_quote=\"true\">" + "> Firefox Firefox Firefox Firefox <br>" + "> Firefox Firefox Firefox Firefox<br>" + "><br>" + ">> Firefox Firefox Firefox Firefox <br>" + ">> Firefox Firefox Firefox Firefox<br>" + "</span></body></html>"); + + ConvertBufToPlainText(test, + nsIDocumentEncoder::OutputFormatted | + nsIDocumentEncoder::OutputCRLineBreak | + nsIDocumentEncoder::OutputLFLineBreak | + nsIDocumentEncoder::OutputFormatFlowed, + kDefaultWrapColumn); + + // create result case + result.AssignLiteral( + "> Firefox Firefox Firefox Firefox \r\n" + "> Firefox Firefox Firefox Firefox\r\n" + ">\r\n" + ">> Firefox Firefox Firefox Firefox \r\n" + ">> Firefox Firefox Firefox Firefox\r\n"); + + ASSERT_TRUE(test.Equals(result)) + << "Wrong HTML to ASCII text serialization " + "with format=flowed; and quoted " + "lines"; +} + +TEST(PlainTextSerializer, PrettyPrintedHtml) +{ + nsString test; + test.AppendLiteral("<html>" NS_LINEBREAK "<body>" NS_LINEBREAK + " first<br>" NS_LINEBREAK " second<br>" NS_LINEBREAK + "</body>" NS_LINEBREAK "</html>"); + + ConvertBufToPlainText(test, 0, kDefaultWrapColumn); + ASSERT_TRUE(test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK)) + << "Wrong prettyprinted html to text serialization"; +} + +TEST(PlainTextSerializer, PreElement) +{ + nsString test; + test.AppendLiteral("<html>" NS_LINEBREAK "<body>" NS_LINEBREAK + "<pre>" NS_LINEBREAK " first" NS_LINEBREAK + " second" NS_LINEBREAK "</pre>" NS_LINEBREAK + "</body>" NS_LINEBREAK "</html>"); + + ConvertBufToPlainText(test, 0, kDefaultWrapColumn); + ASSERT_TRUE(test.EqualsLiteral(" first" NS_LINEBREAK + " second" NS_LINEBREAK NS_LINEBREAK)) + << "Wrong prettyprinted html to text serialization"; +} + +TEST(PlainTextSerializer, BlockElement) +{ + nsString test; + test.AppendLiteral("<html>" NS_LINEBREAK "<body>" NS_LINEBREAK + "<div>" NS_LINEBREAK " first" NS_LINEBREAK + "</div>" NS_LINEBREAK "<div>" NS_LINEBREAK + " second" NS_LINEBREAK "</div>" NS_LINEBREAK + "</body>" NS_LINEBREAK "</html>"); + + ConvertBufToPlainText(test, 0, kDefaultWrapColumn); + ASSERT_TRUE(test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK)) + << "Wrong prettyprinted html to text serialization"; +} + +TEST(PlainTextSerializer, PreWrapElementForThunderbird) +{ + // This test examines the magic pre-wrap setup that Thunderbird relies on. + nsString test; + test.AppendLiteral("<html>" NS_LINEBREAK + "<body style=\"white-space: pre-wrap;\">" NS_LINEBREAK + "<pre>" NS_LINEBREAK + " first line is too long" NS_LINEBREAK + " second line is even loooonger " NS_LINEBREAK + "</pre>" NS_LINEBREAK "</body>" NS_LINEBREAK "</html>"); + + const uint32_t wrapColumn = 10; + ConvertBufToPlainText(test, nsIDocumentEncoder::OutputWrap, wrapColumn); + // "\n\n first\nline is\ntoo long\n second\nline is\neven\nloooonger\n\n\n" + ASSERT_TRUE(test.EqualsLiteral( + NS_LINEBREAK NS_LINEBREAK + " first" NS_LINEBREAK "line is" NS_LINEBREAK "too long" NS_LINEBREAK + " second" NS_LINEBREAK "line is" NS_LINEBREAK "even" NS_LINEBREAK + "loooonger" NS_LINEBREAK NS_LINEBREAK NS_LINEBREAK)) + << "Wrong prettyprinted html to text serialization"; +} + +TEST(PlainTextSerializer, Simple) +{ + nsString test; + test.AppendLiteral( + "<html><base>base</base><head><span>span</span></head>" + "<body>body</body></html>"); + ConvertBufToPlainText(test, 0, kDefaultWrapColumn); + ASSERT_TRUE(test.EqualsLiteral("basespanbody")) + << "Wrong html to text serialization"; +} + +TEST(PlainTextSerializer, OneHundredAndOneOL) +{ + nsAutoString test; + test.AppendLiteral( + "<html>" + "<body>" + "<ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><" + "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><" + "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><" + "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><" + "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><" + "ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol><ol></ol></ol></ol></" + "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></" + "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></" + "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></" + "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></" + "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></" + "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></" + "ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></ol></" + "ol></ol><li>X</li></ol>" + "</body>" + "</html>"); + + ConvertBufToPlainText(test, nsIDocumentEncoder::OutputFormatted, + kDefaultWrapColumn); + + nsAutoString expected; + expected.AppendLiteral(" 1. X" NS_LINEBREAK); + ASSERT_EQ(test, expected); +} diff --git a/dom/base/test/gtest/TestScheduler.cpp b/dom/base/test/gtest/TestScheduler.cpp new file mode 100644 index 0000000000..381d0bce2f --- /dev/null +++ b/dom/base/test/gtest/TestScheduler.cpp @@ -0,0 +1,341 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "mozilla/dom/CCGCScheduler.h" +#include "mozilla/TimeStamp.h" + +// This is a test for mozilla::CCGCScheduler. + +using namespace mozilla; + +static TimeDuration kOneSecond = TimeDuration::FromSeconds(1); +static TimeDuration kTenthSecond = TimeDuration::FromSeconds(0.1); +static TimeDuration kFrameDuration = TimeDuration::FromSeconds(1.0 / 60.0); + +static mozilla::TimeStamp sNow = TimeStamp::Now(); + +static mozilla::TimeStamp sStartupTime = sNow; + +inline mozilla::TimeStamp mozilla::CCGCScheduler::Now() { return sNow; } + +static mozilla::TimeStamp AdvanceTime(TimeDuration aDuration) { + sNow += aDuration; + return sNow; +} + +static uint32_t sSuspected = 0; + +inline uint32_t mozilla::CCGCScheduler::SuspectedCCObjects() { + return sSuspected; +} +static void SetNumSuspected(uint32_t n) { sSuspected = n; } +static void SuspectMore(uint32_t n) { sSuspected += n; } + +using CCRunnerState = mozilla::CCGCScheduler::CCRunnerState; + +static TimeStamp Now() { return sNow; } + +class TestGC { + protected: + CCGCScheduler& mScheduler; + + public: + explicit TestGC(CCGCScheduler& aScheduler) : mScheduler(aScheduler) {} + void Run(int aNumSlices); +}; + +void TestGC::Run(int aNumSlices) { + // Make the purple buffer nearly empty so it is itself not an adequate reason + // for wanting a CC. + static_assert(3 < mozilla::kCCPurpleLimit); + SetNumSuspected(3); + + // Running the GC should not influence whether a CC is currently seen as + // needed. But the first time we run GC, it will be false; later, we will + // have run a GC and set it to true. + bool neededCCAtStartOfGC = mScheduler.IsCCNeeded(); + + mScheduler.NoteGCBegin(); + + for (int slice = 0; slice < aNumSlices; slice++) { + EXPECT_TRUE(mScheduler.InIncrementalGC()); + TimeStamp idleDeadline = Now() + kTenthSecond; + TimeDuration budget = + mScheduler.ComputeInterSliceGCBudget(idleDeadline, Now()); + EXPECT_NEAR(budget.ToSeconds(), 0.1, 1.e-6); + // Pretend the GC took exactly the budget. + AdvanceTime(budget); + + EXPECT_EQ(mScheduler.IsCCNeeded(), neededCCAtStartOfGC); + + // Mutator runs for 1 second. + AdvanceTime(kOneSecond); + } + + mScheduler.NoteGCEnd(); + mScheduler.SetNeedsFullGC(false); +} + +class TestCC { + protected: + CCGCScheduler& mScheduler; + + public: + explicit TestCC(CCGCScheduler& aScheduler) : mScheduler(aScheduler) {} + + void Run(int aNumSlices) { + Prepare(); + MaybePokeCC(); + TimerFires(aNumSlices); + EndCycleCollectionCallback(); + KillCCRunner(); + } + + virtual void Prepare() = 0; + virtual void MaybePokeCC(); + virtual void TimerFires(int aNumSlices); + virtual void RunSlices(int aNumSlices); + virtual void RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd, + int aSliceNum, int aNumSlices) = 0; + virtual void ForgetSkippable(); + virtual void EndCycleCollectionCallback(); + virtual void KillCCRunner(); +}; + +void TestCC::MaybePokeCC() { + // nsJSContext::MaybePokeCC + EXPECT_TRUE(mScheduler.ShouldScheduleCC()); + + mScheduler.InitCCRunnerStateMachine(CCRunnerState::ReducePurple); + EXPECT_TRUE(mScheduler.IsEarlyForgetSkippable()); +} + +void TestCC::TimerFires(int aNumSlices) { + // Series of CCRunner timer fires. + CCRunnerStep step; + + while (true) { + SuspectMore(1000); + TimeStamp idleDeadline = Now() + kOneSecond; + step = mScheduler.GetNextCCRunnerAction(idleDeadline); + // Should first see a series of ForgetSkippable actions. + if (step.mAction != CCRunnerAction::ForgetSkippable || + step.mRemoveChildless != KeepChildless) { + break; + } + EXPECT_EQ(step.mYield, Yield); + ForgetSkippable(); + } + + while (step.mYield == Continue) { + TimeStamp idleDeadline = Now() + kOneSecond; + step = mScheduler.GetNextCCRunnerAction(idleDeadline); + } + EXPECT_EQ(step.mAction, CCRunnerAction::ForgetSkippable); + EXPECT_EQ(step.mRemoveChildless, RemoveChildless); + ForgetSkippable(); + + TimeStamp idleDeadline = Now() + kOneSecond; + step = mScheduler.GetNextCCRunnerAction(idleDeadline); + EXPECT_EQ(step.mAction, CCRunnerAction::CleanupContentUnbinder); + step = mScheduler.GetNextCCRunnerAction(idleDeadline); + EXPECT_EQ(step.mAction, CCRunnerAction::CleanupDeferred); + + RunSlices(aNumSlices); +} + +void TestCC::ForgetSkippable() { + uint32_t suspectedBefore = sSuspected; + // ...ForgetSkippable would happen here... + js::SliceBudget budget = + mScheduler.ComputeForgetSkippableBudget(Now(), Now() + kTenthSecond); + EXPECT_NEAR(budget.timeBudget.budget, kTenthSecond.ToMilliseconds(), 1); + AdvanceTime(kTenthSecond); + mScheduler.NoteForgetSkippableComplete(Now(), suspectedBefore); +} + +void TestCC::RunSlices(int aNumSlices) { + TimeStamp ccStartTime = Now(); + TimeStamp prevSliceEnd = ccStartTime; + for (int ccslice = 0; ccslice < aNumSlices; ccslice++) { + RunSlice(ccStartTime, prevSliceEnd, ccslice, aNumSlices); + prevSliceEnd = Now(); + } + + SetNumSuspected(0); +} + +void TestCC::EndCycleCollectionCallback() { + // nsJSContext::EndCycleCollectionCallback + CycleCollectorResults results; + results.mFreedGCed = 10; + results.mFreedJSZones = 2; + mScheduler.NoteCycleCollected(results); + + // Because > 0 zones were freed. + EXPECT_TRUE(mScheduler.NeedsGCAfterCC()); +} + +void TestCC::KillCCRunner() { + // nsJSContext::KillCCRunner + mScheduler.UnblockCC(); + mScheduler.DeactivateCCRunner(); + mScheduler.NoteCCEnd(Now()); +} + +class TestIdleCC : public TestCC { + public: + explicit TestIdleCC(CCGCScheduler& aScheduler) : TestCC(aScheduler) {} + + void Prepare() override; + void RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd, int aSliceNum, + int aNumSlices) override; +}; + +void TestIdleCC::Prepare() { EXPECT_TRUE(!mScheduler.InIncrementalGC()); } + +void TestIdleCC::RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd, + int aSliceNum, int aNumSlices) { + CCRunnerStep step; + TimeStamp idleDeadline = Now() + kTenthSecond; + + // The scheduler should request a CycleCollect slice. + step = mScheduler.GetNextCCRunnerAction(idleDeadline); + EXPECT_EQ(step.mAction, CCRunnerAction::CycleCollect); + + // nsJSContext::RunCycleCollectorSlice + + EXPECT_FALSE(mScheduler.InIncrementalGC()); + bool preferShorter; + js::SliceBudget budget = mScheduler.ComputeCCSliceBudget( + idleDeadline, aCCStartTime, aPrevSliceEnd, &preferShorter); + // The scheduler will set the budget to our deadline (0.1sec in the future). + EXPECT_NEAR(budget.timeBudget.budget, kTenthSecond.ToMilliseconds(), 1); + EXPECT_FALSE(preferShorter); + + AdvanceTime(kTenthSecond); +} + +class TestNonIdleCC : public TestCC { + public: + explicit TestNonIdleCC(CCGCScheduler& aScheduler) : TestCC(aScheduler) {} + + void Prepare() override; + void RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd, int aSliceNum, + int aNumSlices) override; +}; + +void TestNonIdleCC::Prepare() { + EXPECT_TRUE(!mScheduler.InIncrementalGC()); + + // Advance time by an hour to give time for a user event in the past. + AdvanceTime(TimeDuration::FromSeconds(3600)); +} + +void TestNonIdleCC::RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd, + int aSliceNum, int aNumSlices) { + CCRunnerStep step; + TimeStamp nullDeadline; + + // The scheduler should tell us to run a slice of cycle collection. + step = mScheduler.GetNextCCRunnerAction(nullDeadline); + EXPECT_EQ(step.mAction, CCRunnerAction::CycleCollect); + + // nsJSContext::RunCycleCollectorSlice + + EXPECT_FALSE(mScheduler.InIncrementalGC()); + + bool preferShorter; + js::SliceBudget budget = mScheduler.ComputeCCSliceBudget( + nullDeadline, aCCStartTime, aPrevSliceEnd, &preferShorter); + if (aSliceNum == 0) { + // First slice of the CC, so always use the baseBudget which is + // kICCSliceBudget (3ms) for a non-idle slice. + EXPECT_NEAR(budget.timeBudget.budget, kICCSliceBudget.ToMilliseconds(), + 0.1); + } else if (aSliceNum == 1) { + // Second slice still uses the baseBudget, since not much time has passed + // so none of the lengthening mechanisms have kicked in yet. + EXPECT_NEAR(budget.timeBudget.budget, kICCSliceBudget.ToMilliseconds(), + 0.1); + } else if (aSliceNum == 2) { + // We're not overrunning kMaxICCDuration, so we don't go unlimited. + EXPECT_FALSE(budget.isUnlimited()); + // This slice is delayed by twice the allowed amount. Slice time should be + // doubled. + EXPECT_NEAR(budget.timeBudget.budget, kICCSliceBudget.ToMilliseconds() * 2, + 0.1); + } else { + // We're not overrunning kMaxICCDuration, so we don't go unlimited. + EXPECT_FALSE(budget.isUnlimited()); + + // These slices are not delayed, but enough time has passed that the + // dominating factor is now the linear ramp up to max slice time at the + // halfway point to kMaxICCDuration. + EXPECT_TRUE(budget.timeBudget.budget > kICCSliceBudget.ToMilliseconds()); + EXPECT_TRUE(budget.timeBudget.budget <= + MainThreadIdlePeriod::GetLongIdlePeriod()); + } + EXPECT_TRUE(preferShorter); // Non-idle prefers shorter slices + + AdvanceTime(TimeDuration::FromMilliseconds(budget.timeBudget.budget)); + if (aSliceNum == 1) { + // Delay the third slice (only). + AdvanceTime(kICCIntersliceDelay * 2); + } +} + +// Do a GC then CC then GC. +static bool BasicScenario(CCGCScheduler& aScheduler, TestGC* aTestGC, + TestCC* aTestCC) { + // Run a 10-slice incremental GC. + aTestGC->Run(10); + + // After a GC, the scheduler should decide to do a full CC regardless of the + // number of purple buffer entries. + SetNumSuspected(3); + EXPECT_TRUE(aScheduler.IsCCNeeded()); + + // Now we should want to CC. + EXPECT_TRUE(aScheduler.ShouldScheduleCC()); + + // Do a 5-slice CC. + aTestCC->Run(5); + + // Not enough suspected objects to deserve a CC. + EXPECT_FALSE(aScheduler.IsCCNeeded()); + EXPECT_FALSE(aScheduler.ShouldScheduleCC()); + SetNumSuspected(10000); + + // We shouldn't want to CC again yet, it's too soon. + EXPECT_FALSE(aScheduler.ShouldScheduleCC()); + AdvanceTime(mozilla::kCCDelay); + + // *Now* it's time for another CC. + EXPECT_TRUE(aScheduler.ShouldScheduleCC()); + + // Run a 3-slice incremental GC. + EXPECT_TRUE(!aScheduler.InIncrementalGC()); + aTestGC->Run(3); + + return true; +} + +static CCGCScheduler scheduler; +static TestGC gc(scheduler); +static TestIdleCC ccIdle(scheduler); +static TestNonIdleCC ccNonIdle(scheduler); + +TEST(TestScheduler, Idle) +{ + // Cannot CC until we GC once. + EXPECT_FALSE(scheduler.ShouldScheduleCC()); + + EXPECT_TRUE(BasicScenario(scheduler, &gc, &ccIdle)); +} + +TEST(TestScheduler, NonIdle) +{ EXPECT_TRUE(BasicScenario(scheduler, &gc, &ccNonIdle)); } diff --git a/dom/base/test/gtest/TestXPathGenerator.cpp b/dom/base/test/gtest/TestXPathGenerator.cpp new file mode 100644 index 0000000000..c9f4993179 --- /dev/null +++ b/dom/base/test/gtest/TestXPathGenerator.cpp @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "XPathGenerator.h" +#include "nsString.h" + +TEST(TestXPathGenerator, TestQuoteArgumentWithoutQuote) +{ + nsAutoString arg; + arg.AssignLiteral(u"testing"); + + nsAutoString expectedResult; + expectedResult.AssignLiteral(u"\'testing\'"); + + nsAutoString result; + XPathGenerator::QuoteArgument(arg, result); + + ASSERT_TRUE(expectedResult.Equals(result)); +} + +TEST(TestXPathGenerator, TestQuoteArgumentWithSingleQuote) +{ + nsAutoString arg; + arg.AssignLiteral(u"\'testing\'"); + + nsAutoString expectedResult; + expectedResult.AssignLiteral(u"\"\'testing\'\""); + + nsAutoString result; + XPathGenerator::QuoteArgument(arg, result); + + ASSERT_TRUE(expectedResult.Equals(result)); +} + +TEST(TestXPathGenerator, TestQuoteArgumentWithDoubleQuote) +{ + nsAutoString arg; + arg.AssignLiteral(u"\"testing\""); + + nsAutoString expectedResult; + expectedResult.AssignLiteral(u"\'\"testing\"\'"); + + nsAutoString result; + XPathGenerator::QuoteArgument(arg, result); + + ASSERT_TRUE(expectedResult.Equals(result)); +} + +TEST(TestXPathGenerator, TestQuoteArgumentWithSingleAndDoubleQuote) +{ + nsAutoString arg; + arg.AssignLiteral(u"\'testing\""); + + nsAutoString expectedResult; + expectedResult.AssignLiteral(u"concat(\'\',\"\'\",\'testing\"\')"); + + nsAutoString result; + XPathGenerator::QuoteArgument(arg, result); + printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(), + NS_ConvertUTF16toUTF8(expectedResult).get()); + + ASSERT_TRUE(expectedResult.Equals(result)); +} + +TEST(TestXPathGenerator, + TestQuoteArgumentWithDoubleQuoteAndASequenceOfSingleQuote) +{ + nsAutoString arg; + arg.AssignLiteral(u"\'\'\'\'testing\""); + + nsAutoString expectedResult; + expectedResult.AssignLiteral(u"concat(\'\',\"\'\'\'\'\",\'testing\"\')"); + + nsAutoString result; + XPathGenerator::QuoteArgument(arg, result); + printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(), + NS_ConvertUTF16toUTF8(expectedResult).get()); + + ASSERT_TRUE(expectedResult.Equals(result)); +} + +TEST(TestXPathGenerator, + TestQuoteArgumentWithDoubleQuoteAndTwoSequencesOfSingleQuote) +{ + nsAutoString arg; + arg.AssignLiteral(u"\'\'\'\'testing\'\'\'\'\'\'\""); + + nsAutoString expectedResult; + expectedResult.AssignLiteral( + u"concat(\'\',\"\'\'\'\'\",\'testing\',\"\'\'\'\'\'\'\",\'\"\')"); + + nsAutoString result; + XPathGenerator::QuoteArgument(arg, result); + printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(), + NS_ConvertUTF16toUTF8(expectedResult).get()); + + ASSERT_TRUE(expectedResult.Equals(result)); +} + +TEST(TestXPathGenerator, + TestQuoteArgumentWithDoubleQuoteAndTwoSequencesOfSingleQuoteInMiddle) +{ + nsAutoString arg; + arg.AssignLiteral(u"t\'\'\'\'estin\'\'\'\'\'\'\"g"); + + nsAutoString expectedResult; + expectedResult.AssignLiteral( + u"concat(\'t\',\"\'\'\'\'\",\'estin\',\"\'\'\'\'\'\'\",\'\"g\')"); + + nsAutoString result; + XPathGenerator::QuoteArgument(arg, result); + printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(), + NS_ConvertUTF16toUTF8(expectedResult).get()); + + ASSERT_TRUE(expectedResult.Equals(result)); +} + +TEST(TestXPathGenerator, TestEscapeNameWithNormalCharacters) +{ + nsAutoString arg; + arg.AssignLiteral(u"testing"); + + nsAutoString expectedResult; + expectedResult.AssignLiteral(u"testing"); + + nsAutoString result; + XPathGenerator::EscapeName(arg, result); + + ASSERT_TRUE(expectedResult.Equals(result)); +} + +TEST(TestXPathGenerator, TestEscapeNameWithSpecialCharacters) +{ + nsAutoString arg; + arg.AssignLiteral(u"^testing!"); + + nsAutoString expectedResult; + expectedResult.AssignLiteral(u"*[local-name()=\'^testing!\']"); + + nsAutoString result; + XPathGenerator::EscapeName(arg, result); + printf("Result: %s\nExpected: %s\n", NS_ConvertUTF16toUTF8(result).get(), + NS_ConvertUTF16toUTF8(expectedResult).get()); + + ASSERT_TRUE(expectedResult.Equals(result)); +} diff --git a/dom/base/test/gtest/moz.build b/dom/base/test/gtest/moz.build new file mode 100644 index 0000000000..561cef5ae4 --- /dev/null +++ b/dom/base/test/gtest/moz.build @@ -0,0 +1,20 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + "TestContentUtils.cpp", + "TestMimeType.cpp", + "TestParser.cpp", + "TestPlainTextSerializer.cpp", + "TestScheduler.cpp", + "TestXPathGenerator.cpp", +] + +LOCAL_INCLUDES += ["/dom/base"] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" |