test-intl.cc 10.9 KB
Newer Older
1 2 3 4 5 6
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef V8_INTL_SUPPORT

7
#include "src/objects/intl-objects.h"
8
#include "src/objects/js-break-iterator.h"
9
#include "src/objects/js-collator-inl.h"
10 11
#include "src/objects/js-date-time-format.h"
#include "src/objects/js-list-format.h"
12
#include "src/objects/js-number-format.h"
13 14 15
#include "src/objects/js-plural-rules.h"
#include "src/objects/js-relative-time-format.h"
#include "src/objects/js-segmenter.h"
16
#include "src/objects/lookup.h"
17
#include "src/objects/objects-inl.h"
18
#include "src/objects/option-utils.h"
19
#include "test/cctest/cctest.h"
20
#include "unicode/coll.h"
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

namespace v8 {
namespace internal {

// This operator overloading enables CHECK_EQ to be used with
// std::vector<NumberFormatSpan>
bool operator==(const NumberFormatSpan& lhs, const NumberFormatSpan& rhs) {
  return memcmp(&lhs, &rhs, sizeof(lhs)) == 0;
}
template <typename _CharT, typename _Traits>
std::basic_ostream<_CharT, _Traits>& operator<<(
    std::basic_ostream<_CharT, _Traits>& self, const NumberFormatSpan& part) {
  return self << "{" << part.field_id << "," << part.begin_pos << ","
              << part.end_pos << "}";
}

void test_flatten_regions_to_parts(
    const std::vector<NumberFormatSpan>& regions,
    const std::vector<NumberFormatSpan>& expected_parts) {
  std::vector<NumberFormatSpan> mutable_regions = regions;
  std::vector<NumberFormatSpan> parts = FlattenRegionsToParts(&mutable_regions);
  CHECK_EQ(expected_parts, parts);
}

TEST(FlattenRegionsToParts) {
  test_flatten_regions_to_parts(
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(-1, 0, 10), NumberFormatSpan(1, 2, 8),
          NumberFormatSpan(2, 2, 4), NumberFormatSpan(3, 6, 8),
      },
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(-1, 0, 2), NumberFormatSpan(2, 2, 4),
          NumberFormatSpan(1, 4, 6), NumberFormatSpan(3, 6, 8),
          NumberFormatSpan(-1, 8, 10),
      });
  test_flatten_regions_to_parts(
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(0, 0, 1),
      },
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(0, 0, 1),
      });
  test_flatten_regions_to_parts(
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(-1, 0, 1), NumberFormatSpan(0, 0, 1),
      },
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(0, 0, 1),
      });
  test_flatten_regions_to_parts(
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(0, 0, 1), NumberFormatSpan(-1, 0, 1),
      },
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(0, 0, 1),
      });
  test_flatten_regions_to_parts(
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(-1, 0, 10), NumberFormatSpan(1, 0, 1),
          NumberFormatSpan(2, 0, 2), NumberFormatSpan(3, 0, 3),
          NumberFormatSpan(4, 0, 4), NumberFormatSpan(5, 0, 5),
          NumberFormatSpan(15, 5, 10), NumberFormatSpan(16, 6, 10),
          NumberFormatSpan(17, 7, 10), NumberFormatSpan(18, 8, 10),
          NumberFormatSpan(19, 9, 10),
      },
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(1, 0, 1), NumberFormatSpan(2, 1, 2),
          NumberFormatSpan(3, 2, 3), NumberFormatSpan(4, 3, 4),
          NumberFormatSpan(5, 4, 5), NumberFormatSpan(15, 5, 6),
          NumberFormatSpan(16, 6, 7), NumberFormatSpan(17, 7, 8),
          NumberFormatSpan(18, 8, 9), NumberFormatSpan(19, 9, 10),
      });

  //              :          4
  //              :      22 33    3
  //              :      11111   22
  // input regions:     0000000  111
  //              :     ------------
  // output parts:      0221340--231
  test_flatten_regions_to_parts(
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(-1, 0, 12), NumberFormatSpan(0, 0, 7),
          NumberFormatSpan(1, 9, 12), NumberFormatSpan(1, 1, 6),
          NumberFormatSpan(2, 9, 11), NumberFormatSpan(2, 1, 3),
          NumberFormatSpan(3, 10, 11), NumberFormatSpan(3, 4, 6),
          NumberFormatSpan(4, 5, 6),
      },
      std::vector<NumberFormatSpan>{
          NumberFormatSpan(0, 0, 1), NumberFormatSpan(2, 1, 3),
          NumberFormatSpan(1, 3, 4), NumberFormatSpan(3, 4, 5),
          NumberFormatSpan(4, 5, 6), NumberFormatSpan(0, 6, 7),
          NumberFormatSpan(-1, 7, 9), NumberFormatSpan(2, 9, 10),
          NumberFormatSpan(3, 10, 11), NumberFormatSpan(1, 11, 12),
      });
}

117
TEST(GetStringOption) {
118 119 120 121 122 123
  LocalContext env;
  Isolate* isolate = CcTest::i_isolate();
  v8::Isolate* v8_isolate = env->GetIsolate();
  v8::HandleScope handle_scope(v8_isolate);

  Handle<JSObject> options = isolate->factory()->NewJSObjectWithNullProto();
124 125 126 127
  {
    // No value found
    std::unique_ptr<char[]> result = nullptr;
    Maybe<bool> found =
128 129
        GetStringOption(isolate, options, "foo", std::vector<const char*>{},
                        "service", &result);
130 131 132 133
    CHECK(!found.FromJust());
    CHECK_NULL(result);
  }

134
  Handle<String> key = isolate->factory()->NewStringFromAsciiChecked("foo");
135
  LookupIterator it(isolate, options, key);
136
  CHECK(Object::SetProperty(&it, Handle<Smi>(Smi::FromInt(42), isolate),
137
                            StoreOrigin::kMaybeKeyed,
138
                            Just(ShouldThrow::kThrowOnError))
139
            .FromJust());
140 141 142 143 144

  {
    // Value found
    std::unique_ptr<char[]> result = nullptr;
    Maybe<bool> found =
145 146
        GetStringOption(isolate, options, "foo", std::vector<const char*>{},
                        "service", &result);
147 148 149 150 151
    CHECK(found.FromJust());
    CHECK_NOT_NULL(result);
    CHECK_EQ(0, strcmp("42", result.get()));
  }

152
  {
153 154
    // No expected value in values array
    std::unique_ptr<char[]> result = nullptr;
155 156 157
    Maybe<bool> found =
        GetStringOption(isolate, options, "foo",
                        std::vector<const char*>{"bar"}, "service", &result);
158
    CHECK(isolate->has_pending_exception());
159 160
    CHECK(found.IsNothing());
    CHECK_NULL(result);
161 162 163
    isolate->clear_pending_exception();
  }

164 165 166
  {
    // Expected value in values array
    std::unique_ptr<char[]> result = nullptr;
167 168 169
    Maybe<bool> found =
        GetStringOption(isolate, options, "foo", std::vector<const char*>{"42"},
                        "service", &result);
170 171 172 173 174
    CHECK(found.FromJust());
    CHECK_NOT_NULL(result);
    CHECK_EQ(0, strcmp("42", result.get()));
  }
}
175

176 177 178 179 180 181 182 183 184 185
TEST(GetBoolOption) {
  LocalContext env;
  Isolate* isolate = CcTest::i_isolate();
  v8::Isolate* v8_isolate = env->GetIsolate();
  v8::HandleScope handle_scope(v8_isolate);

  Handle<JSObject> options = isolate->factory()->NewJSObjectWithNullProto();
  {
    bool result = false;
    Maybe<bool> found =
186
        GetBoolOption(isolate, options, "foo", "service", &result);
187 188 189 190 191 192
    CHECK(!found.FromJust());
    CHECK(!result);
  }

  Handle<String> key = isolate->factory()->NewStringFromAsciiChecked("foo");
  {
193
    LookupIterator it(isolate, options, key);
194 195
    Handle<Object> false_value =
        handle(i::ReadOnlyRoots(isolate).false_value(), isolate);
196
    Object::SetProperty(isolate, options, key, false_value,
197 198
                        StoreOrigin::kMaybeKeyed,
                        Just(ShouldThrow::kThrowOnError))
199 200 201
        .Assert();
    bool result = false;
    Maybe<bool> found =
202
        GetBoolOption(isolate, options, "foo", "service", &result);
203 204 205 206 207
    CHECK(found.FromJust());
    CHECK(!result);
  }

  {
208
    LookupIterator it(isolate, options, key);
209 210
    Handle<Object> true_value =
        handle(i::ReadOnlyRoots(isolate).true_value(), isolate);
211
    Object::SetProperty(isolate, options, key, true_value,
212 213
                        StoreOrigin::kMaybeKeyed,
                        Just(ShouldThrow::kThrowOnError))
214 215 216
        .Assert();
    bool result = false;
    Maybe<bool> found =
217
        GetBoolOption(isolate, options, "foo", "service", &result);
218 219 220
    CHECK(found.FromJust());
    CHECK(result);
  }
221 222
}

223 224 225
TEST(GetAvailableLocales) {
  std::set<std::string> locales;

226
  locales = JSV8BreakIterator::GetAvailableLocales();
227 228 229
  CHECK(locales.count("en-US"));
  CHECK(!locales.count("abcdefg"));

230
  locales = JSCollator::GetAvailableLocales();
231 232
  CHECK(locales.count("en-US"));

233
  locales = JSDateTimeFormat::GetAvailableLocales();
234 235
  CHECK(locales.count("en-US"));

236
  locales = JSListFormat::GetAvailableLocales();
237
  CHECK(locales.count("en-US"));
238

239
  locales = JSNumberFormat::GetAvailableLocales();
240 241
  CHECK(locales.count("en-US"));

242
  locales = JSPluralRules::GetAvailableLocales();
243
  CHECK(locales.count("en"));
244 245 246 247 248 249 250

  locales = JSRelativeTimeFormat::GetAvailableLocales();
  CHECK(locales.count("en-US"));

  locales = JSSegmenter::GetAvailableLocales();
  CHECK(locales.count("en-US"));
  CHECK(!locales.count("abcdefg"));
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
// Tests that the LocaleCompare fast path and generic path return the same
// comparison results for all ASCII strings.
TEST(StringLocaleCompareFastPath) {
  LocalContext env;
  Isolate* isolate = CcTest::i_isolate();
  HandleScope handle_scope(isolate);

  // We compare all single-char strings of printable ASCII characters.
  std::vector<Handle<String>> ascii_strings;
  for (int c = 0; c <= 0x7F; c++) {
    if (!std::isprint(c)) continue;
    ascii_strings.push_back(
        isolate->factory()->LookupSingleCharacterStringFromCode(c));
  }

  Handle<JSFunction> collator_constructor = Handle<JSFunction>(
      JSFunction::cast(
          isolate->context().native_context().intl_collator_function()),
      isolate);
  Handle<Map> constructor_map =
      JSFunction::GetDerivedMap(isolate, collator_constructor,
                                collator_constructor)
          .ToHandleChecked();
  Handle<Object> options(ReadOnlyRoots(isolate).undefined_value(), isolate);
  static const char* const kMethodName = "StringLocaleCompareFastPath";

  // For all fast locales, exhaustively compare within the printable ASCII
  // range.
  const std::set<std::string>& locales = JSCollator::GetAvailableLocales();
  for (const std::string& locale : locales) {
    Handle<String> locale_string =
        isolate->factory()->NewStringFromAsciiChecked(locale.c_str());

286 287
    if (Intl::CompareStringsOptionsFor(isolate->AsLocalIsolate(), locale_string,
                                       options) !=
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
        Intl::CompareStringsOptions::kTryFastPath) {
      continue;
    }

    Handle<JSCollator> collator =
        JSCollator::New(isolate, constructor_map, locale_string, options,
                        kMethodName)
            .ToHandleChecked();

    for (size_t i = 0; i < ascii_strings.size(); i++) {
      Handle<String> lhs = ascii_strings[i];
      for (size_t j = i + 1; j < ascii_strings.size(); j++) {
        Handle<String> rhs = ascii_strings[j];
        CHECK_EQ(
            Intl::CompareStrings(isolate, *collator->icu_collator().raw(), lhs,
                                 rhs, Intl::CompareStringsOptions::kNone),
            Intl::CompareStrings(isolate, *collator->icu_collator().raw(), lhs,
                                 rhs,
                                 Intl::CompareStringsOptions::kTryFastPath));
      }
    }
  }
}

312 313 314 315
}  // namespace internal
}  // namespace v8

#endif  // V8_INTL_SUPPORT