Bug 1425280 - add a test to catch localization note mismatch
MozReview-Commit-ID: JHY75LzfPIa
--- a/browser/base/content/test/static/browser.ini
+++ b/browser/base/content/test/static/browser.ini
@@ -3,15 +3,16 @@
# Since the specific flavor doesn't matter from a correctness standpoint,
# just skip the tests on ASAN and debug builds. Also known to be flaky on
# Linux32 (bug 1172468, bug 1349307), so skip them there as well.
skip-if = asan || debug || (os == 'linux' && bits == 32)
support-files =
head.js
[browser_all_files_referenced.js]
+[browser_localization_notes.js]
[browser_misused_characters_in_strings.js]
support-files =
bug1262648_string_with_newlines.dtd
[browser_parsable_css.js]
support-files =
dummy_page.html
[browser_parsable_script.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/static/browser_localization_notes.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Check against typical mistakes in localization notes:
+ * - check if a localization note key does not match any definition in the file
+ * - check if a localization note key is duplicated in the file
+ *
+ * This runs agains both DTDs and properties files.
+ */
+
+var checkDTDs = async function(uriSpec) {
+ let rawContents = await fetchFile(uriSpec);
+ let fileName = uriSpec.match(/[^/\\]+$/)[0];
+
+ let notes = rawContents.match(/LOCALIZATION NOTE\s+\(([\w\.]*)\)/g) || [];
+ for (let note of notes) {
+ let [, key] = note.match(/LOCALIZATION NOTE\s+\(([\w\.]*)\)/);
+
+ info(`Testing file : ${fileName}, key: ${key}`);
+
+ // Find all entity definitions for a given key
+ let entityMatch = rawContents.match(new RegExp(`<!ENTITY\\s+${key}\\s`, "g"));
+
+ // Some localization note keys are only the "prefix" of actual keys
+ // (eg okButton for okButton.title, okButton.label, etc...).
+ let categoryMatch = rawContents.match(new RegExp(`<!ENTITY\\s+${key}\\.`, "g"));
+
+ if (entityMatch && entityMatch.length != 1) {
+ ok(false, `Entity only found once (${key} in ${fileName})`);
+ }
+
+ if (!entityMatch && !categoryMatch) {
+ ok(false, `No entity found for localization note (${key} in ${fileName})`);
+ }
+
+ // Check if the localization note appears several times for the same key.
+ let noteRegExp = new RegExp(`LOCALIZATION NOTE\\s+\\(${key}\\)`, "g");
+ let noteMatch = rawContents.match(noteRegExp);
+ if (noteMatch.length != 1) {
+ ok(false, `No duplicated localization notes (${key} in ${fileName})`);
+ }
+ }
+};
+
+var checkProperties = async function(uriSpec) {
+ let rawContents = await fetchFile(uriSpec);
+ let fileName = uriSpec.match(/[^/\\]+$/)[0];
+
+ let notes = rawContents.match(/LOCALIZATION NOTE\s+\(([\w\.]*)\)/g) || [];
+ for (let note of notes) {
+ let [, key] = note.match(/LOCALIZATION NOTE\s+\(([\w\.]*)\)/);
+
+ info(`Testing file : ${fileName}, key: ${key}`);
+
+ // Find all entity definitions for a given key
+ let entityRegExp = new RegExp(`(\\n|^)\\s*${key}\\s*=`, "g");
+ let entityMatch = rawContents.match(entityRegExp);
+
+ // Some localization note keys are only the "prefix" of actual keys
+ // (eg okButton for okButton.title, okButton.label, etc...).
+ let categoryRegExp = new RegExp(`(\\n|^)\\s*${key}\\.[\\w\\.]+\\s*=`, "g");
+ let categoryMatch = rawContents.match(categoryRegExp);
+
+ if (entityMatch && entityMatch.length != 1) {
+ ok(false, `Entity only found once (${key} in ${fileName})`);
+ }
+
+ if (!entityMatch && !categoryMatch) {
+ ok(false, `No entity found for localization note (${key} in ${fileName})`);
+ }
+
+ let noteRegExp = new RegExp(`LOCALIZATION NOTE\\s+\\(${key}\\)`, "g");
+ let noteMatch = rawContents.match(noteRegExp);
+ if (noteMatch.length != 1) {
+ ok(false, `No duplicated localization notes (${key} in ${fileName})`);
+ }
+ }
+};
+
+add_task(async function checkAllTheDTDs() {
+ let uris = await getAllTheFiles(".dtd");
+ ok(uris.length, `Found ${uris.length} .dtd files to scan for localization note issues`);
+ for (let uri of uris) {
+ await checkDTDs(uri.spec);
+ }
+});
+
+add_task(async function checkAllTheProperties() {
+ let uris = await getAllTheFiles(".properties");
+ ok(uris.length,
+ `Found ${uris.length} .properties files to scan for localization note issues`);
+ for (let uri of uris) {
+ await checkProperties(uri.spec);
+ }
+});
--- a/browser/base/content/test/static/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js
@@ -187,30 +187,16 @@ function testForError(filepath, key, str
function testForErrors(filepath, key, str) {
testForError(filepath, key, str, /\w'\w/, "apostrophe", "Strings with apostrophes should use foo\u2019s instead of foo's.");
testForError(filepath, key, str, /\w\u2018\w/, "incorrect-apostrophe", "Strings with apostrophes should use foo\u2019s instead of foo\u2018s.");
testForError(filepath, key, str, /'.+'/, "single-quote", "Single-quoted strings should use Unicode \u2018foo\u2019 instead of 'foo'.");
testForError(filepath, key, str, /"/, "double-quote", "Double-quoted strings should use Unicode \u201cfoo\u201d instead of \"foo\".");
testForError(filepath, key, str, /\.\.\./, "ellipsis", "Strings with an ellipsis should use the Unicode \u2026 character instead of three periods.");
}
-async function getAllTheFiles(extension) {
- let appDirGreD = Services.dirsvc.get("GreD", Ci.nsIFile);
- let appDirXCurProcD = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
- if (appDirGreD.contains(appDirXCurProcD)) {
- return generateURIsFromDirTree(appDirGreD, [extension]);
- }
- if (appDirXCurProcD.contains(appDirGreD)) {
- return generateURIsFromDirTree(appDirXCurProcD, [extension]);
- }
- let urisGreD = await generateURIsFromDirTree(appDirGreD, [extension]);
- let urisXCurProcD = await generateURIsFromDirTree(appDirXCurProcD, [extension]);
- return Array.from(new Set(urisGreD.concat(appDirXCurProcD)));
-}
-
add_task(async function checkAllTheProperties() {
// This asynchronously produces a list of URLs (sadly, mostly sync on our
// test infrastructure because it runs against jarfiles there, and
// our zipreader APIs are all sync)
let uris = await getAllTheFiles(".properties");
ok(uris.length, `Found ${uris.length} .properties files to scan for misused characters`);
for (let uri of uris) {
--- a/browser/base/content/test/static/head.js
+++ b/browser/base/content/test/static/head.js
@@ -143,8 +143,21 @@ function fetchFile(uri) {
};
xhr.onerror = error => {
ok(false, `XHR error reading ${uri}: ${error}`);
resolve("");
};
xhr.send(null);
});
}
+
+async function getAllTheFiles(extension) {
+ let appDirGreD = Services.dirsvc.get("GreD", Ci.nsIFile);
+ let appDirXCurProcD = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
+ if (appDirGreD.contains(appDirXCurProcD)) {
+ return generateURIsFromDirTree(appDirGreD, [extension]);
+ }
+ if (appDirXCurProcD.contains(appDirGreD)) {
+ return generateURIsFromDirTree(appDirXCurProcD, [extension]);
+ }
+ let urisGreD = await generateURIsFromDirTree(appDirGreD, [extension]);
+ return Array.from(new Set(urisGreD.concat(appDirXCurProcD)));
+}