1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "platform/text/LocaleWin.h"
33
34#include "platform/DateComponents.h"
35#include "wtf/DateMath.h"
36#include "wtf/MathExtras.h"
37#include "wtf/PassOwnPtr.h"
38#include "wtf/text/CString.h"
39#include <gtest/gtest.h>
40
41using namespace blink;
42using namespace std;
43
44class LocaleWinTest : public ::testing::Test {
45protected:
46    enum {
47        January = 0, February, March,
48        April, May, June,
49        July, August, September,
50        October, November, December,
51    };
52
53    enum {
54        Sunday = 0, Monday, Tuesday,
55        Wednesday, Thursday, Friday,
56        Saturday,
57    };
58
59    // See http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
60    // Note that some locales are country-neutral.
61    enum {
62        ArabicEG = 0x0C01, // ar-eg
63        ChineseCN = 0x0804, // zh-cn
64        ChineseHK = 0x0C04, // zh-hk
65        ChineseTW = 0x0404, // zh-tw
66        German = 0x0407, // de
67        EnglishUS = 0x409, // en-us
68        FrenchFR = 0x40C, // fr
69        JapaneseJP = 0x411, // ja
70        KoreanKR = 0x0412, // ko
71        Persian = 0x0429, // fa
72        Spanish = 0x040A, // es
73    };
74
75    DateComponents dateComponents(int year, int month, int day)
76    {
77        DateComponents date;
78        date.setMillisecondsSinceEpochForDate(msForDate(year, month, day));
79        return date;
80    }
81
82    double msForDate(int year, int month, int day)
83    {
84        return dateToDaysFrom1970(year, month, day) * msPerDay;
85    }
86
87    String formatDate(LCID lcid, int year, int month, int day)
88    {
89        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
90        return locale->formatDateTime(dateComponents(year, month, day));
91    }
92
93    unsigned firstDayOfWeek(LCID lcid)
94    {
95        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
96        return locale->firstDayOfWeek();
97    }
98
99    String monthLabel(LCID lcid, unsigned index)
100    {
101        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
102        return locale->monthLabels()[index];
103    }
104
105    String weekDayShortLabel(LCID lcid, unsigned index)
106    {
107        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
108        return locale->weekDayShortLabels()[index];
109    }
110
111    bool isRTL(LCID lcid)
112    {
113        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
114        return locale->isRTL();
115    }
116
117#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
118    String monthFormat(LCID lcid)
119    {
120        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
121        return locale->monthFormat();
122    }
123
124    String timeFormat(LCID lcid)
125    {
126        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
127        return locale->timeFormat();
128    }
129
130    String shortTimeFormat(LCID lcid)
131    {
132        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
133        return locale->shortTimeFormat();
134    }
135
136    String shortMonthLabel(LCID lcid, unsigned index)
137    {
138        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
139        return locale->shortMonthLabels()[index];
140    }
141
142    String timeAMPMLabel(LCID lcid, unsigned index)
143    {
144        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
145        return locale->timeAMPMLabels()[index];
146    }
147
148    String decimalSeparator(LCID lcid)
149    {
150        OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
151        return locale->localizedDecimalSeparator();
152    }
153#endif
154};
155
156TEST_F(LocaleWinTest, formatDate)
157{
158    EXPECT_STREQ("04/27/2005", formatDate(EnglishUS, 2005, April, 27).utf8().data());
159    EXPECT_STREQ("27/04/2005", formatDate(FrenchFR, 2005, April, 27).utf8().data());
160    EXPECT_STREQ("2005/04/27", formatDate(JapaneseJP, 2005, April, 27).utf8().data());
161}
162
163TEST_F(LocaleWinTest, firstDayOfWeek)
164{
165    EXPECT_EQ(Sunday, firstDayOfWeek(EnglishUS));
166    EXPECT_EQ(Monday, firstDayOfWeek(FrenchFR));
167    EXPECT_EQ(Sunday, firstDayOfWeek(JapaneseJP));
168}
169
170TEST_F(LocaleWinTest, monthLabels)
171{
172    EXPECT_STREQ("January", monthLabel(EnglishUS, January).utf8().data());
173    EXPECT_STREQ("June", monthLabel(EnglishUS, June).utf8().data());
174    EXPECT_STREQ("December", monthLabel(EnglishUS, December).utf8().data());
175
176    EXPECT_STREQ("janvier", monthLabel(FrenchFR, January).utf8().data());
177    EXPECT_STREQ("juin", monthLabel(FrenchFR, June).utf8().data());
178    EXPECT_STREQ("d\xC3\xA9" "cembre", monthLabel(FrenchFR, December).utf8().data());
179
180    EXPECT_STREQ("1\xE6\x9C\x88", monthLabel(JapaneseJP, January).utf8().data());
181    EXPECT_STREQ("6\xE6\x9C\x88", monthLabel(JapaneseJP, June).utf8().data());
182    EXPECT_STREQ("12\xE6\x9C\x88", monthLabel(JapaneseJP, December).utf8().data());
183}
184
185TEST_F(LocaleWinTest, weekDayShortLabels)
186{
187    EXPECT_STREQ("Sun", weekDayShortLabel(EnglishUS, Sunday).utf8().data());
188    EXPECT_STREQ("Wed", weekDayShortLabel(EnglishUS, Wednesday).utf8().data());
189    EXPECT_STREQ("Sat", weekDayShortLabel(EnglishUS, Saturday).utf8().data());
190
191    EXPECT_STREQ("dim.", weekDayShortLabel(FrenchFR, Sunday).utf8().data());
192    EXPECT_STREQ("mer.", weekDayShortLabel(FrenchFR, Wednesday).utf8().data());
193    EXPECT_STREQ("sam.", weekDayShortLabel(FrenchFR, Saturday).utf8().data());
194
195    EXPECT_STREQ("\xE6\x97\xA5", weekDayShortLabel(JapaneseJP, Sunday).utf8().data());
196    EXPECT_STREQ("\xE6\xB0\xB4", weekDayShortLabel(JapaneseJP, Wednesday).utf8().data());
197    EXPECT_STREQ("\xE5\x9C\x9F", weekDayShortLabel(JapaneseJP, Saturday).utf8().data());
198}
199
200TEST_F(LocaleWinTest, isRTL)
201{
202    EXPECT_TRUE(isRTL(ArabicEG));
203    EXPECT_FALSE(isRTL(EnglishUS));
204}
205
206#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
207TEST_F(LocaleWinTest, dateFormat)
208{
209    EXPECT_STREQ("y-M-d", LocaleWin::dateFormat("y-M-d").utf8().data());
210    EXPECT_STREQ("''yy'-'''MM'''-'dd", LocaleWin::dateFormat("''yy-''MM''-dd").utf8().data());
211    EXPECT_STREQ("yyyy'-''''-'MMM'''''-'dd", LocaleWin::dateFormat("yyyy-''''-MMM''''-dd").utf8().data());
212    EXPECT_STREQ("yyyy'-'''''MMMM-dd", LocaleWin::dateFormat("yyyy-''''MMMM-dd").utf8().data());
213}
214
215TEST_F(LocaleWinTest, monthFormat)
216{
217    EXPECT_STREQ("MMMM, yyyy", monthFormat(EnglishUS).utf8().data());
218    EXPECT_STREQ("MMMM yyyy", monthFormat(FrenchFR).utf8().data());
219    EXPECT_STREQ("yyyy\xE5\xB9\xB4M\xE6\x9C\x88", monthFormat(JapaneseJP).utf8().data());
220}
221
222TEST_F(LocaleWinTest, timeFormat)
223{
224    EXPECT_STREQ("h:mm:ss a", timeFormat(EnglishUS).utf8().data());
225    EXPECT_STREQ("HH:mm:ss", timeFormat(FrenchFR).utf8().data());
226    EXPECT_STREQ("H:mm:ss", timeFormat(JapaneseJP).utf8().data());
227}
228
229TEST_F(LocaleWinTest, shortTimeFormat)
230{
231    EXPECT_STREQ("h:mm a", shortTimeFormat(EnglishUS).utf8().data());
232    EXPECT_STREQ("HH:mm", shortTimeFormat(FrenchFR).utf8().data());
233    EXPECT_STREQ("H:mm", shortTimeFormat(JapaneseJP).utf8().data());
234}
235
236TEST_F(LocaleWinTest, shortMonthLabels)
237{
238    EXPECT_STREQ("Jan", shortMonthLabel(EnglishUS, 0).utf8().data());
239    EXPECT_STREQ("Dec", shortMonthLabel(EnglishUS, 11).utf8().data());
240    EXPECT_STREQ("janv.", shortMonthLabel(FrenchFR, 0).utf8().data());
241    EXPECT_STREQ("d\xC3\xA9" "c.", shortMonthLabel(FrenchFR, 11).utf8().data());
242    EXPECT_STREQ("1", shortMonthLabel(JapaneseJP, 0).utf8().data());
243    EXPECT_STREQ("12", shortMonthLabel(JapaneseJP, 11).utf8().data());
244}
245
246TEST_F(LocaleWinTest, timeAMPMLabels)
247{
248    EXPECT_STREQ("AM", timeAMPMLabel(EnglishUS, 0).utf8().data());
249    EXPECT_STREQ("PM", timeAMPMLabel(EnglishUS, 1).utf8().data());
250
251    EXPECT_STREQ("", timeAMPMLabel(FrenchFR, 0).utf8().data());
252    EXPECT_STREQ("", timeAMPMLabel(FrenchFR, 1).utf8().data());
253
254    EXPECT_STREQ("\xE5\x8D\x88\xE5\x89\x8D", timeAMPMLabel(JapaneseJP, 0).utf8().data());
255    EXPECT_STREQ("\xE5\x8D\x88\xE5\xBE\x8C", timeAMPMLabel(JapaneseJP, 1).utf8().data());
256}
257
258TEST_F(LocaleWinTest, decimalSeparator)
259{
260    EXPECT_STREQ(".", decimalSeparator(EnglishUS).utf8().data());
261    EXPECT_STREQ(",", decimalSeparator(FrenchFR).utf8().data());
262}
263#endif
264
265static void testNumberIsReversible(LCID lcid, const char* original, const char* shouldHave = 0)
266{
267    OwnPtr<LocaleWin> locale = LocaleWin::create(lcid, true /* defaultsForLocale */);
268    String localized = locale->convertToLocalizedNumber(original);
269    if (shouldHave)
270        EXPECT_TRUE(localized.contains(shouldHave));
271    String converted = locale->convertFromLocalizedNumber(localized);
272    EXPECT_STREQ(original, converted.utf8().data());
273}
274
275void testNumbers(LCID lcid)
276{
277    testNumberIsReversible(lcid, "123456789012345678901234567890");
278    testNumberIsReversible(lcid, "-123.456");
279    testNumberIsReversible(lcid, ".456");
280    testNumberIsReversible(lcid, "-0.456");
281}
282
283TEST_F(LocaleWinTest, localizedNumberRoundTrip)
284{
285    testNumberIsReversible(EnglishUS, "123456789012345678901234567890");
286    testNumberIsReversible(EnglishUS, "-123.456", ".");
287    testNumberIsReversible(EnglishUS, ".456", ".");
288    testNumberIsReversible(EnglishUS, "-0.456", ".");
289
290    testNumberIsReversible(FrenchFR, "123456789012345678901234567890");
291    testNumberIsReversible(FrenchFR, "-123.456", ",");
292    testNumberIsReversible(FrenchFR, ".456", ",");
293    testNumberIsReversible(FrenchFR, "-0.456", ",");
294
295    // Test some of major locales.
296    testNumbers(ArabicEG);
297    testNumbers(German);
298    testNumbers(Spanish);
299    testNumbers(Persian);
300    testNumbers(JapaneseJP);
301    testNumbers(KoreanKR);
302    testNumbers(ChineseCN);
303    testNumbers(ChineseHK);
304    testNumbers(ChineseTW);
305}
306