bug 1388789, add tests for nsTextFormatter to reproduce badness that can come from l10n draft
authorAxel Hecht <axel@pike.org>
Wed, 09 Aug 2017 18:25:22 +0200
changeset 644359 18e4f61062867c69f8ee9438ca1845d8a5ffc36f
parent 641632 47248637eafa9a38dade8dc3aa6c4736177c8d8d
child 725592 778a0d9ca39b71e36ca519e2e5fa6e9481971819
push id73419
push useraxel@mozilla.com
push dateThu, 10 Aug 2017 22:03:23 +0000
bugs1388789
milestone57.0a1
bug 1388789, add tests for nsTextFormatter to reproduce badness that can come from l10n Adding tests for the most commonly used formats: duxsSc. yes, char, too. Also adding tests about various mixups between ordered and non-ordered format lists, and missing references. MozReview-Commit-ID: 8cwQtUA6xZu
xpcom/tests/gtest/TestTextFormatter.cpp
--- a/xpcom/tests/gtest/TestTextFormatter.cpp
+++ b/xpcom/tests/gtest/TestTextFormatter.cpp
@@ -1,17 +1,35 @@
 /* -*- Mode: C++; tab-width: 8; 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 "nsTextFormatter.h"
 #include "nsString.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsCOMPtr.h"
+#include "nsICrashReporter.h"
+#include "nsServiceManagerUtils.h"
+#endif
+
 #include "gtest/gtest.h"
 
+void DisableCrashReporterForTextFormatter()
+{
+#ifdef MOZ_CRASHREPORTER
+    nsCOMPtr<nsICrashReporter> crashreporter =
+        do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+    if (crashreporter) {
+      crashreporter->SetEnabled(false);
+    }
+#endif
+}
+
 TEST(TextFormatter, Tests)
 {
   nsAutoString fmt(NS_LITERAL_STRING("%3$s %4$S %1$d %2$d %2$d %3$s"));
   char utf8[] = "Hello";
   char16_t ucs2[]={'W', 'o', 'r', 'l', 'd', 0x4e00, 0xAc00, 0xFF45, 0x0103, 0x00};
   int d=3;
 
   char16_t buf[256];
@@ -27,8 +45,177 @@ TEST(TextFormatter, Tests)
                                 0x33, 0x33, 0x20, 0x48, 0x65, 0x6C,
                                 0x6C, 0x6F};
 
   for (uint32_t i=0; i<out.Length(); i++) {
     ASSERT_EQ(uout[i], expected[i]);
   }
 }
 
+/*
+ * The tests below validate checks we run on localizations against the
+ * implementation of the nsTextFormatter.
+ * If they need fixing, please CC l10n@mozilla.com to the bug,
+ * to evaluate the necessary changes to the l10n ecosystem and checks.
+ *
+ * EXPECT_STRNE cases need checks in compare-locales, and of course
+ * EXPECT_DEATH, too.
+ */
+
+/*
+ * Check misordered parameters
+ */
+
+TEST(TextFormatterOrdering, orders)
+{
+  nsString out;
+
+  // plain list
+  out.Adopt(nsTextFormatter::smprintf(u"%S %S %S", u"1", u"2", u"3"));
+  EXPECT_STREQ("1 2 3", NS_ConvertUTF16toUTF8(out).get());
+
+  // ordered list
+  out.Adopt(nsTextFormatter::smprintf(u"%2$S %3$S %1$S", u"1", u"2", u"3"));
+  EXPECT_STREQ("2 3 1", NS_ConvertUTF16toUTF8(out).get());
+
+  // mixed ordered list and non-ordered
+  // This hits an MOZ_ASSERT. TODO
+  out.Adopt(nsTextFormatter::smprintf(u"%2S %S %1$S", u"1", u"2", u"3"));
+  EXPECT_STRNE("2 3 1", NS_ConvertUTF16toUTF8(out).get());
+
+  // Referencing an extra param returns empty strings
+  out.Adopt(nsTextFormatter::smprintf(u" %2$S ", u"1"));
+  EXPECT_STRNE(" 1 ", NS_ConvertUTF16toUTF8(out).get());
+
+  // Double referencing existing argument works
+  out.Adopt(nsTextFormatter::smprintf(u"%1$S %1$S", u"1"));
+  EXPECT_STREQ("1 1", NS_ConvertUTF16toUTF8(out).get());
+
+  // Dropping trailing argument works
+  out.Adopt(nsTextFormatter::smprintf(u" %1$S ", u"1", u"2"));
+  EXPECT_STREQ(" 1 ", NS_ConvertUTF16toUTF8(out).get());
+
+  // Dropping leading arguments doesn't
+  out.Adopt(nsTextFormatter::smprintf(u" %2$S ", u"1", u"2"));
+  EXPECT_STRNE(" 2 ", NS_ConvertUTF16toUTF8(out).get());
+
+  // Dropping middle arguments doesn't
+  out.Adopt(nsTextFormatter::smprintf(u" %3$S %1$S ", u"1", u"2", u"3"));
+  EXPECT_STRNE(" 3 1 ", NS_ConvertUTF16toUTF8(out).get());
+}
+
+/*
+ * Tests to validate that horrible things happen if the passed-in
+ * variable and the formatter don't match.
+ */
+TEST(TextFormatterTestMismatch, format_d)
+{
+  DisableCrashReporterForTextFormatter();
+  NS_NAMED_LITERAL_STRING(fmt, "%d");
+  nsString out;
+  // just for completeness, this is our format, and works
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), int(-1)));
+  EXPECT_STREQ("-1", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), (uint32_t)-1));
+  EXPECT_STRNE("360999", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), float(3.5)));
+  EXPECT_STRNE("3.5", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), "foo"));
+  EXPECT_STRNE("foo", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), u"foo"));
+  EXPECT_STRNE("foo", NS_ConvertUTF16toUTF8(out).get());
+}
+
+TEST(TextFormatterTestMismatch, format_u)
+{
+  DisableCrashReporterForTextFormatter();
+  NS_NAMED_LITERAL_STRING(fmt, "%u");
+  nsString out;
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), int(-1)));
+  EXPECT_STRNE("-1", NS_ConvertUTF16toUTF8(out).get());
+  // just for completeness, this is our format, and works
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), (uint32_t)-1));
+  EXPECT_STREQ("4294967295", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), float(3.5)));
+  EXPECT_STRNE("3.5", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), "foo"));
+  EXPECT_STRNE("foo", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), u"foo"));
+  EXPECT_STRNE("foo", NS_ConvertUTF16toUTF8(out).get());
+}
+
+TEST(TextFormatterTestMismatch, format_x)
+{
+  DisableCrashReporterForTextFormatter();
+  NS_NAMED_LITERAL_STRING(fmt, "%x");
+  nsString out;
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), int(-1)));
+  // not clear if this is intended or not, but it's not completely garbage
+  EXPECT_STREQ("ffffffff", NS_ConvertUTF16toUTF8(out).get());
+  // just for completeness, this is our format, and works
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), (uint32_t)-1));
+  EXPECT_STREQ("ffffffff", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), float(3.5)));
+  EXPECT_STRNE("3.5", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), "foo"));
+  EXPECT_STRNE("foo", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), u"foo"));
+  EXPECT_STRNE("foo", NS_ConvertUTF16toUTF8(out).get());
+}
+
+TEST(TextFormatterTestMismatch, format_s)
+{
+  DisableCrashReporterForTextFormatter();
+  NS_NAMED_LITERAL_STRING(fmt, "%s");
+  nsString out;
+  EXPECT_DEATH_IF_SUPPORTED(nsTextFormatter::smprintf(fmt.get(), int(-1)), "");
+  EXPECT_DEATH_IF_SUPPORTED(nsTextFormatter::smprintf(fmt.get(), (uint32_t)-1), "");
+  EXPECT_DEATH_IF_SUPPORTED(nsTextFormatter::smprintf(fmt.get(), float(3.5)), "");
+  // just for completeness, this is our format, and works
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), "foo"));
+  EXPECT_STREQ("foo", NS_ConvertUTF16toUTF8(out).get());
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), u"foo"));
+  EXPECT_STRNE("foo", NS_LossyConvertUTF16toASCII(out).get());
+}
+
+TEST(TextFormatterTestMismatch, format_S)
+{
+  DisableCrashReporterForTextFormatter();
+  NS_NAMED_LITERAL_STRING(fmt, "%S");
+  nsString out;
+  EXPECT_DEATH_IF_SUPPORTED(nsTextFormatter::smprintf(fmt.get(), int32_t(-1)), "");
+  EXPECT_DEATH_IF_SUPPORTED(nsTextFormatter::smprintf(fmt.get(), (uint32_t)-1), "");
+  EXPECT_DEATH_IF_SUPPORTED(nsTextFormatter::smprintf(fmt.get(), float(3.5)), "");
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), "foo"));
+  EXPECT_STRNE("foo", NS_ConvertUTF16toUTF8(out).get());
+  // just for completeness, this is our format, and works
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), u"foo"));
+  EXPECT_STREQ("foo", NS_ConvertUTF16toUTF8(out).get());
+}
+
+TEST(TextFormatterTestMismatch, format_c)
+{
+  DisableCrashReporterForTextFormatter();
+  NS_NAMED_LITERAL_STRING(fmt, "%c");
+  nsString out;
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), int32_t(-1)));
+  EXPECT_EQ(1u, out.Length());
+  EXPECT_EQ((uint16_t)-1, out.CharAt(0));  // not useful for humans :-/
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), (uint32_t)-1));
+  EXPECT_EQ(1u, out.Length());
+  EXPECT_EQ((uint16_t)-1, out.CharAt(0));  // not useful for humans :-/
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), float(3.5)));
+  EXPECT_NE(1u, out.Length());  // not even the length works here :-?
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), "foo"));
+  EXPECT_EQ(1u, out.Length());
+  EXPECT_NE(u'f', out.CharAt(0));
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), u"foo"));
+  EXPECT_EQ(1u, out.Length());
+  EXPECT_NE(u'f', out.CharAt(0));
+
+  // just for completeness, this is our format, and works
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), 'c'));
+  EXPECT_EQ(1u, out.Length());
+  EXPECT_EQ(u'c', out.CharAt(0));
+  out.Adopt(nsTextFormatter::smprintf(fmt.get(), u'c'));
+  EXPECT_EQ(1u, out.Length());
+  EXPECT_EQ(u'c', out.CharAt(0));
+}