summaryrefslogtreecommitdiffstats
path: root/storage/test/unit/test_locale_collation.js
blob: 4576f39a0eb440ded3190352869530750fdd2c3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
 * 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/. */

/**
 * Bug 499990 - Locale-aware collation
 *
 * Tests our custom, locale-aware collating sequences.
 */

// The name of the file containing the strings we'll sort during this test.
// The file's data is taken from intl/locale/tests/sort/us-ascii_base.txt and
// and intl/locale/tests/sort/us-ascii_sort.txt.
const DATA_BASENAME = "locale_collation.txt";

// The test data from DATA_BASENAME is read into this array.
var gStrings;

// A connection to our in-memory UTF-16-encoded database.
var gUtf16Conn;

// Helper Functions

/**
 * Since we create a UTF-16 database we have to clean it up, in addition to
 * the normal cleanup of Storage tests.
 */
function cleanupLocaleTests() {
  print("-- Cleaning up test_locale_collation.js suite.");
  gUtf16Conn.close();
  cleanup();
}

/**
 * Creates a test database similar to the default one created in
 * head_storage.js, except that this one uses UTF-16 encoding.
 *
 * @return A connection to the database.
 */
function createUtf16Database() {
  print("Creating the in-memory UTF-16-encoded database.");
  let conn = Services.storage.openSpecialDatabase("memory");
  conn.executeSimpleSQL("PRAGMA encoding = 'UTF-16'");

  print("Make sure the encoding was set correctly and is now UTF-16.");
  let stmt = conn.createStatement("PRAGMA encoding");
  Assert.ok(stmt.executeStep());
  let enc = stmt.getString(0);
  stmt.finalize();

  // The value returned will actually be UTF-16le or UTF-16be.
  Assert.ok(enc === "UTF-16le" || enc === "UTF-16be");

  return conn;
}

/**
 * Compares aActual to aExpected, ensuring that the numbers and orderings of
 * the two arrays' elements are the same.
 *
 * @param aActual
 *        An array of strings retrieved from the database.
 * @param aExpected
 *        An array of strings to which aActual should be equivalent.
 */
function ensureResultsAreCorrect(aActual, aExpected) {
  print("Actual results:   " + aActual);
  print("Expected results: " + aExpected);

  Assert.equal(aActual.length, aExpected.length);
  for (let i = 0; i < aActual.length; i++) {
    Assert.equal(aActual[i], aExpected[i]);
  }
}

/**
 * Synchronously SELECTs all rows from the test table of the given database
 * using the given collation.
 *
 * @param  aCollation
 *         The name of one of our custom locale collations.  The rows are
 *         ordered by this collation.
 * @param  aConn
 *         A connection to either the UTF-8 database or the UTF-16 database.
 * @return The resulting strings in an array.
 */
function getResults(aCollation, aConn) {
  let results = [];
  let stmt = aConn.createStatement(
    "SELECT t FROM test ORDER BY t COLLATE " + aCollation + " ASC"
  );
  while (stmt.executeStep()) {
    results.push(stmt.row.t);
  }
  stmt.finalize();
  return results;
}

/**
 * Inserts strings into our test table of the given database in the order given.
 *
 * @param aStrings
 *        An array of strings.
 * @param aConn
 *        A connection to either the UTF-8 database or the UTF-16 database.
 */
function initTableWithStrings(aStrings, aConn) {
  print("Initializing test table.");

  aConn.executeSimpleSQL("DROP TABLE IF EXISTS test");
  aConn.createTable("test", "t TEXT");
  let stmt = aConn.createStatement("INSERT INTO test (t) VALUES (:t)");
  aStrings.forEach(function(str) {
    stmt.params.t = str;
    stmt.execute();
    stmt.reset();
  });
  stmt.finalize();
}

/**
 * Returns a sorting function suitable for passing to Array.prototype.sort().
 * The returned function uses the application's locale to compare strings.
 *
 * @param  aCollation
 *         The name of one of our custom locale collations.  The sorting
 *         strength is computed from this value.
 * @return A function to use as a sorting callback.
 */
function localeCompare(aCollation) {
  let sensitivity;

  switch (aCollation) {
    case "locale":
      sensitivity = "base";
      break;
    case "locale_case_sensitive":
      sensitivity = "case";
      break;
    case "locale_accent_sensitive":
      sensitivity = "accent";
      break;
    case "locale_case_accent_sensitive":
      sensitivity = "variant";
      break;
    default:
      do_throw("Error in test: unknown collation '" + aCollation + "'");
      break;
  }
  const collation = new Intl.Collator("en", { sensitivity });
  return function(aStr1, aStr2) {
    return collation.compare(aStr1, aStr2);
  };
}

/**
 * Reads in the test data from the file DATA_BASENAME and returns it as an array
 * of strings.
 *
 * @return The test data as an array of strings.
 */
function readTestData() {
  print("Reading in test data.");

  let file = do_get_file(DATA_BASENAME);

  let istream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
    Ci.nsIFileInputStream
  );
  istream.init(file, -1, -1, 0);
  istream.QueryInterface(Ci.nsILineInputStream);

  let line = {};
  let lines = [];
  while (istream.readLine(line)) {
    lines.push(line.value);
  }
  istream.close();

  return lines;
}

/**
 * Gets the results from the given database using the given collation and
 * ensures that they match gStrings sorted by the same collation.
 *
 * @param aCollation
 *        The name of one of our custom locale collations.  The rows from the
 *        database and the expected results are ordered by this collation.
 * @param aConn
 *        A connection to either the UTF-8 database or the UTF-16 database.
 */
function runTest(aCollation, aConn) {
  ensureResultsAreCorrect(
    getResults(aCollation, aConn),
    gStrings.slice(0).sort(localeCompare(aCollation))
  );
}

/**
 * Gets the results from the UTF-8 database using the given collation and
 * ensures that they match gStrings sorted by the same collation.
 *
 * @param aCollation
 *        The name of one of our custom locale collations.  The rows from the
 *        database and the expected results are ordered by this collation.
 */
function runUtf8Test(aCollation) {
  runTest(aCollation, getOpenedDatabase());
}

/**
 * Gets the results from the UTF-16 database using the given collation and
 * ensures that they match gStrings sorted by the same collation.
 *
 * @param aCollation
 *        The name of one of our custom locale collations.  The rows from the
 *        database and the expected results are ordered by this collation.
 */
function runUtf16Test(aCollation) {
  runTest(aCollation, gUtf16Conn);
}

/**
 * Sets up the test suite.
 */
function setup() {
  print("-- Setting up the test_locale_collation.js suite.");

  gStrings = readTestData();

  initTableWithStrings(gStrings, getOpenedDatabase());

  gUtf16Conn = createUtf16Database();
  initTableWithStrings(gStrings, gUtf16Conn);
}

// Test Runs

var gTests = [
  {
    desc: "Case and accent sensitive UTF-8",
    run: () => runUtf8Test("locale_case_accent_sensitive"),
  },

  {
    desc: "Case sensitive, accent insensitive UTF-8",
    run: () => runUtf8Test("locale_case_sensitive"),
  },

  {
    desc: "Case insensitive, accent sensitive UTF-8",
    run: () => runUtf8Test("locale_accent_sensitive"),
  },

  {
    desc: "Case and accent insensitive UTF-8",
    run: () => runUtf8Test("locale"),
  },

  {
    desc: "Case and accent sensitive UTF-16",
    run: () => runUtf16Test("locale_case_accent_sensitive"),
  },

  {
    desc: "Case sensitive, accent insensitive UTF-16",
    run: () => runUtf16Test("locale_case_sensitive"),
  },

  {
    desc: "Case insensitive, accent sensitive UTF-16",
    run: () => runUtf16Test("locale_accent_sensitive"),
  },

  {
    desc: "Case and accent insensitive UTF-16",
    run: () => runUtf16Test("locale"),
  },
];

function run_test() {
  setup();
  gTests.forEach(function(test) {
    print("-- Running test: " + test.desc);
    test.run();
  });
  cleanupLocaleTests();
}