test_locale_collation.js (8035B)
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- 2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /** 8 * Bug 499990 - Locale-aware collation 9 * 10 * Tests our custom, locale-aware collating sequences. 11 */ 12 13 // The name of the file containing the strings we'll sort during this test. 14 // The file's data is taken from intl/locale/tests/sort/us-ascii_base.txt and 15 // and intl/locale/tests/sort/us-ascii_sort.txt. 16 const DATA_BASENAME = "locale_collation.txt"; 17 18 // The test data from DATA_BASENAME is read into this array. 19 var gStrings; 20 21 // A connection to our in-memory UTF-16-encoded database. 22 var gUtf16Conn; 23 24 // Helper Functions 25 26 /** 27 * Since we create a UTF-16 database we have to clean it up, in addition to 28 * the normal cleanup of Storage tests. 29 */ 30 function cleanupLocaleTests() { 31 print("-- Cleaning up test_locale_collation.js suite."); 32 gUtf16Conn.close(); 33 cleanup(); 34 } 35 36 /** 37 * Creates a test database similar to the default one created in 38 * head_storage.js, except that this one uses UTF-16 encoding. 39 * 40 * @return A connection to the database. 41 */ 42 function createUtf16Database() { 43 print("Creating the in-memory UTF-16-encoded database."); 44 let conn = Services.storage.openSpecialDatabase("memory"); 45 conn.executeSimpleSQL("PRAGMA encoding = 'UTF-16'"); 46 47 print("Make sure the encoding was set correctly and is now UTF-16."); 48 let stmt = conn.createStatement("PRAGMA encoding"); 49 Assert.ok(stmt.executeStep()); 50 let enc = stmt.getString(0); 51 stmt.finalize(); 52 53 // The value returned will actually be UTF-16le or UTF-16be. 54 Assert.ok(enc === "UTF-16le" || enc === "UTF-16be"); 55 56 return conn; 57 } 58 59 /** 60 * Compares aActual to aExpected, ensuring that the numbers and orderings of 61 * the two arrays' elements are the same. 62 * 63 * @param aActual 64 * An array of strings retrieved from the database. 65 * @param aExpected 66 * An array of strings to which aActual should be equivalent. 67 */ 68 function ensureResultsAreCorrect(aActual, aExpected) { 69 print("Actual results: " + aActual); 70 print("Expected results: " + aExpected); 71 72 Assert.equal(aActual.length, aExpected.length); 73 for (let i = 0; i < aActual.length; i++) { 74 Assert.equal(aActual[i], aExpected[i]); 75 } 76 } 77 78 /** 79 * Synchronously SELECTs all rows from the test table of the given database 80 * using the given collation. 81 * 82 * @param aCollation 83 * The name of one of our custom locale collations. The rows are 84 * ordered by this collation. 85 * @param aConn 86 * A connection to either the UTF-8 database or the UTF-16 database. 87 * @return The resulting strings in an array. 88 */ 89 function getResults(aCollation, aConn) { 90 let results = []; 91 let stmt = aConn.createStatement( 92 "SELECT t FROM test ORDER BY t COLLATE " + aCollation + " ASC" 93 ); 94 while (stmt.executeStep()) { 95 results.push(stmt.row.t); 96 } 97 stmt.finalize(); 98 return results; 99 } 100 101 /** 102 * Inserts strings into our test table of the given database in the order given. 103 * 104 * @param aStrings 105 * An array of strings. 106 * @param aConn 107 * A connection to either the UTF-8 database or the UTF-16 database. 108 */ 109 function initTableWithStrings(aStrings, aConn) { 110 print("Initializing test table."); 111 112 aConn.executeSimpleSQL("DROP TABLE IF EXISTS test"); 113 aConn.createTable("test", "t TEXT"); 114 let stmt = aConn.createStatement("INSERT INTO test (t) VALUES (:t)"); 115 aStrings.forEach(function (str) { 116 stmt.params.t = str; 117 stmt.execute(); 118 stmt.reset(); 119 }); 120 stmt.finalize(); 121 } 122 123 /** 124 * Returns a sorting function suitable for passing to Array.prototype.sort(). 125 * The returned function uses the application's locale to compare strings. 126 * 127 * @param aCollation 128 * The name of one of our custom locale collations. The sorting 129 * strength is computed from this value. 130 * @return A function to use as a sorting callback. 131 */ 132 function localeCompare(aCollation) { 133 let sensitivity; 134 135 switch (aCollation) { 136 case "locale": 137 sensitivity = "base"; 138 break; 139 case "locale_case_sensitive": 140 sensitivity = "case"; 141 break; 142 case "locale_accent_sensitive": 143 sensitivity = "accent"; 144 break; 145 case "locale_case_accent_sensitive": 146 sensitivity = "variant"; 147 break; 148 default: 149 do_throw("Error in test: unknown collation '" + aCollation + "'"); 150 break; 151 } 152 const collation = new Intl.Collator("en", { sensitivity }); 153 return function (aStr1, aStr2) { 154 return collation.compare(aStr1, aStr2); 155 }; 156 } 157 158 /** 159 * Reads in the test data from the file DATA_BASENAME and returns it as an array 160 * of strings. 161 * 162 * @return The test data as an array of strings. 163 */ 164 function readTestData() { 165 print("Reading in test data."); 166 167 let file = do_get_file(DATA_BASENAME); 168 169 let istream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance( 170 Ci.nsIFileInputStream 171 ); 172 istream.init(file, -1, -1, 0); 173 istream.QueryInterface(Ci.nsILineInputStream); 174 175 let line = {}; 176 let lines = []; 177 while (istream.readLine(line)) { 178 lines.push(line.value); 179 } 180 istream.close(); 181 182 return lines; 183 } 184 185 /** 186 * Gets the results from the given database using the given collation and 187 * ensures that they match gStrings sorted by the same collation. 188 * 189 * @param aCollation 190 * The name of one of our custom locale collations. The rows from the 191 * database and the expected results are ordered by this collation. 192 * @param aConn 193 * A connection to either the UTF-8 database or the UTF-16 database. 194 */ 195 function runTest(aCollation, aConn) { 196 ensureResultsAreCorrect( 197 getResults(aCollation, aConn), 198 gStrings.slice(0).sort(localeCompare(aCollation)) 199 ); 200 } 201 202 /** 203 * Gets the results from the UTF-8 database using the given collation and 204 * ensures that they match gStrings sorted by the same collation. 205 * 206 * @param aCollation 207 * The name of one of our custom locale collations. The rows from the 208 * database and the expected results are ordered by this collation. 209 */ 210 function runUtf8Test(aCollation) { 211 runTest(aCollation, getOpenedDatabase()); 212 } 213 214 /** 215 * Gets the results from the UTF-16 database using the given collation and 216 * ensures that they match gStrings sorted by the same collation. 217 * 218 * @param aCollation 219 * The name of one of our custom locale collations. The rows from the 220 * database and the expected results are ordered by this collation. 221 */ 222 function runUtf16Test(aCollation) { 223 runTest(aCollation, gUtf16Conn); 224 } 225 226 /** 227 * Sets up the test suite. 228 */ 229 function setup() { 230 print("-- Setting up the test_locale_collation.js suite."); 231 232 gStrings = readTestData(); 233 234 initTableWithStrings(gStrings, getOpenedDatabase()); 235 236 gUtf16Conn = createUtf16Database(); 237 initTableWithStrings(gStrings, gUtf16Conn); 238 } 239 240 // Test Runs 241 242 var gTests = [ 243 { 244 desc: "Case and accent sensitive UTF-8", 245 run: () => runUtf8Test("locale_case_accent_sensitive"), 246 }, 247 248 { 249 desc: "Case sensitive, accent insensitive UTF-8", 250 run: () => runUtf8Test("locale_case_sensitive"), 251 }, 252 253 { 254 desc: "Case insensitive, accent sensitive UTF-8", 255 run: () => runUtf8Test("locale_accent_sensitive"), 256 }, 257 258 { 259 desc: "Case and accent insensitive UTF-8", 260 run: () => runUtf8Test("locale"), 261 }, 262 263 { 264 desc: "Case and accent sensitive UTF-16", 265 run: () => runUtf16Test("locale_case_accent_sensitive"), 266 }, 267 268 { 269 desc: "Case sensitive, accent insensitive UTF-16", 270 run: () => runUtf16Test("locale_case_sensitive"), 271 }, 272 273 { 274 desc: "Case insensitive, accent sensitive UTF-16", 275 run: () => runUtf16Test("locale_accent_sensitive"), 276 }, 277 278 { 279 desc: "Case and accent insensitive UTF-16", 280 run: () => runUtf16Test("locale"), 281 }, 282 ]; 283 284 function run_test() { 285 setup(); 286 gTests.forEach(function (test) { 287 print("-- Running test: " + test.desc); 288 test.run(); 289 }); 290 cleanupLocaleTests(); 291 }