1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/********************************************************************
4 * COPYRIGHT:
5 * Copyright (c) 2015, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
8
9#include "datadrivennumberformattestsuite.h"
10
11#if !UCONFIG_NO_FORMATTING
12
13#include "charstr.h"
14#include "ucbuf.h"
15#include "unicode/localpointer.h"
16#include "ustrfmt.h"
17
18static UBool isCROrLF(UChar c) { return c == 0xa || c == 0xd; }
19static UBool isSpace(UChar c) { return c == 9 || c == 0x20 || c == 0x3000; }
20
21void DataDrivenNumberFormatTestSuite::run(const char *fileName, UBool runAllTests) {
22    fFileLineNumber = 0;
23    fFormatTestNumber = 0;
24    UErrorCode status = U_ZERO_ERROR;
25    for (int32_t i = 0; i < UPRV_LENGTHOF(fPreviousFormatters); ++i) {
26        delete fPreviousFormatters[i];
27        fPreviousFormatters[i] = newFormatter(status);
28    }
29    if (!assertSuccess("Can't create previous formatters", status)) {
30        return;
31    }
32    CharString path(getSourceTestData(status), status);
33    path.appendPathPart(fileName, status);
34    const char *codePage = "UTF-8";
35    LocalUCHARBUFPointer f(ucbuf_open(path.data(), &codePage, TRUE, FALSE, &status));
36    if (!assertSuccess("Can't open data file", status)) {
37        return;
38    }
39    UnicodeString columnValues[kNumberFormatTestTupleFieldCount];
40    ENumberFormatTestTupleField columnTypes[kNumberFormatTestTupleFieldCount];
41    int32_t columnCount;
42    int32_t state = 0;
43    while(U_SUCCESS(status)) {
44        // Read a new line if necessary.
45        if(fFileLine.isEmpty()) {
46            if(!readLine(f.getAlias(), status)) { break; }
47            if (fFileLine.isEmpty() && state == 2) {
48                state = 0;
49            }
50            continue;
51        }
52        if (fFileLine.startsWith("//")) {
53            fFileLine.remove();
54            continue;
55        }
56        // Initial setup of test.
57        if (state == 0) {
58            if (fFileLine.startsWith(UNICODE_STRING("test ", 5))) {
59                fFileTestName = fFileLine;
60                fTuple.clear();
61            } else if(fFileLine.startsWith(UNICODE_STRING("set ", 4))) {
62                setTupleField(status);
63            } else if(fFileLine.startsWith(UNICODE_STRING("begin", 5))) {
64                state = 1;
65            } else {
66                showError("Unrecognized verb.");
67                return;
68            }
69        // column specification
70        } else if (state == 1) {
71            columnCount = splitBy(columnValues, UPRV_LENGTHOF(columnValues), 0x9);
72            for (int32_t i = 0; i < columnCount; ++i) {
73                columnTypes[i] = NumberFormatTestTuple::getFieldByName(
74                    columnValues[i]);
75                if (columnTypes[i] == kNumberFormatTestTupleFieldCount) {
76                    showError("Unrecognized field name.");
77                    return;
78                }
79            }
80            state = 2;
81        // run the tests
82        } else {
83            int32_t columnsInThisRow = splitBy(columnValues, columnCount, 0x9);
84            for (int32_t i = 0; i < columnsInThisRow; ++i) {
85                fTuple.setField(
86                        columnTypes[i], columnValues[i].unescape(), status);
87            }
88            for (int32_t i = columnsInThisRow; i < columnCount; ++i) {
89                fTuple.clearField(columnTypes[i], status);
90            }
91            if (U_FAILURE(status)) {
92                showError("Invalid column values");
93                return;
94            }
95            if (runAllTests || !breaksC()) {
96                UnicodeString errorMessage;
97                UBool shouldFail = (NFTT_GET_FIELD(fTuple, output, "") == "fail")
98                        ? !breaksC()
99                        : breaksC();
100                UBool actualSuccess = isPass(fTuple, errorMessage, status);
101                if (shouldFail && actualSuccess) {
102                    showFailure("Expected failure, but passed");
103                    break;
104                } else if (!shouldFail && !actualSuccess) {
105                    showFailure(errorMessage);
106                    break;
107                }
108                status = U_ZERO_ERROR;
109            }
110        }
111        fFileLine.remove();
112    }
113}
114
115DataDrivenNumberFormatTestSuite::~DataDrivenNumberFormatTestSuite() {
116    for (int32_t i = 0; i < UPRV_LENGTHOF(fPreviousFormatters); ++i) {
117        delete fPreviousFormatters[i];
118    }
119}
120
121UBool DataDrivenNumberFormatTestSuite::breaksC() {
122    return (NFTT_GET_FIELD(fTuple, breaks, "").toUpper().indexOf((UChar)0x43) != -1);
123}
124
125void DataDrivenNumberFormatTestSuite::setTupleField(UErrorCode &status) {
126    if (U_FAILURE(status)) {
127        return;
128    }
129    UnicodeString parts[3];
130    int32_t partCount = splitBy(parts, UPRV_LENGTHOF(parts), 0x20);
131    if (partCount < 3) {
132        showError("Set expects 2 parameters");
133        status = U_PARSE_ERROR;
134        return;
135    }
136    if (!fTuple.setField(
137            NumberFormatTestTuple::getFieldByName(parts[1]),
138            parts[2].unescape(),
139            status)) {
140        showError("Invalid field value");
141    }
142}
143
144
145int32_t
146DataDrivenNumberFormatTestSuite::splitBy(
147        UnicodeString *columnValues,
148        int32_t columnValuesCount,
149        UChar delimiter) {
150    int32_t colIdx = 0;
151    int32_t colStart = 0;
152    int32_t len = fFileLine.length();
153    for (int32_t idx = 0; colIdx < columnValuesCount - 1 && idx < len; ++idx) {
154        UChar ch = fFileLine.charAt(idx);
155        if (ch == delimiter) {
156            columnValues[colIdx++] =
157                    fFileLine.tempSubString(colStart, idx - colStart);
158            colStart = idx + 1;
159        }
160    }
161    columnValues[colIdx++] =
162            fFileLine.tempSubString(colStart, len - colStart);
163    return colIdx;
164}
165
166void DataDrivenNumberFormatTestSuite::showLineInfo() {
167    UnicodeString indent("    ");
168    infoln(indent + fFileTestName);
169    infoln(indent + fFileLine);
170}
171
172void DataDrivenNumberFormatTestSuite::showError(const char *message) {
173    errln("line %d: %s", (int) fFileLineNumber, message);
174    showLineInfo();
175}
176
177void DataDrivenNumberFormatTestSuite::showFailure(const UnicodeString &message) {
178    UChar lineStr[20];
179    uprv_itou(
180            lineStr, UPRV_LENGTHOF(lineStr), (uint32_t) fFileLineNumber, 10, 1);
181    UnicodeString fullMessage("line ");
182    dataerrln(fullMessage.append(lineStr).append(": ")
183            .append(prettify(message)));
184    showLineInfo();
185}
186
187UBool DataDrivenNumberFormatTestSuite::readLine(
188        UCHARBUF *f, UErrorCode &status) {
189    int32_t lineLength;
190    const UChar *line = ucbuf_readline(f, &lineLength, &status);
191    if(line == NULL || U_FAILURE(status)) {
192        if (U_FAILURE(status)) {
193            errln("Error reading line from file.");
194        }
195        fFileLine.remove();
196        return FALSE;
197    }
198    ++fFileLineNumber;
199    // Strip trailing CR/LF, comments, and spaces.
200    while(lineLength > 0 && isCROrLF(line[lineLength - 1])) { --lineLength; }
201    fFileLine.setTo(FALSE, line, lineLength);
202    while(lineLength > 0 && isSpace(line[lineLength - 1])) { --lineLength; }
203    if (lineLength == 0) {
204        fFileLine.remove();
205    }
206    return TRUE;
207}
208
209UBool DataDrivenNumberFormatTestSuite::isPass(
210        const NumberFormatTestTuple &tuple,
211        UnicodeString &appendErrorMessage,
212        UErrorCode &status) {
213    if (U_FAILURE(status)) {
214        return FALSE;
215    }
216    UBool result = FALSE;
217    if (tuple.formatFlag && tuple.outputFlag) {
218        ++fFormatTestNumber;
219        result = isFormatPass(
220                tuple,
221                fPreviousFormatters[
222                        fFormatTestNumber % UPRV_LENGTHOF(fPreviousFormatters)],
223                appendErrorMessage,
224                status);
225    }
226    else if (tuple.toPatternFlag || tuple.toLocalizedPatternFlag) {
227        result = isToPatternPass(tuple, appendErrorMessage, status);
228    } else if (tuple.parseFlag && tuple.outputFlag && tuple.outputCurrencyFlag) {
229        result = isParseCurrencyPass(tuple, appendErrorMessage, status);
230
231    } else if (tuple.parseFlag && tuple.outputFlag) {
232        result = isParsePass(tuple, appendErrorMessage, status);
233    } else if (tuple.pluralFlag) {
234        result = isSelectPass(tuple, appendErrorMessage, status);
235    } else {
236        appendErrorMessage.append("Unrecognized test type.");
237        status = U_ILLEGAL_ARGUMENT_ERROR;
238    }
239    if (!result) {
240        if (appendErrorMessage.length() > 0) {
241            appendErrorMessage.append(": ");
242        }
243        if (U_FAILURE(status)) {
244            appendErrorMessage.append(u_errorName(status));
245            appendErrorMessage.append(": ");
246        }
247        tuple.toString(appendErrorMessage);
248    }
249    return result;
250}
251
252UBool DataDrivenNumberFormatTestSuite::isFormatPass(
253        const NumberFormatTestTuple & /* tuple */,
254        UnicodeString & /*appendErrorMessage*/,
255        UErrorCode &status) {
256    if (U_FAILURE(status)) {
257        return FALSE;
258    }
259    return TRUE;
260}
261
262UBool DataDrivenNumberFormatTestSuite::isFormatPass(
263        const NumberFormatTestTuple &tuple,
264        UObject * /* somePreviousFormatter */,
265        UnicodeString &appendErrorMessage,
266        UErrorCode &status) {
267    return isFormatPass(tuple, appendErrorMessage, status);
268}
269
270UObject *DataDrivenNumberFormatTestSuite::newFormatter(
271        UErrorCode & /*status*/) {
272    return NULL;
273}
274
275UBool DataDrivenNumberFormatTestSuite::isToPatternPass(
276        const NumberFormatTestTuple & /* tuple */,
277        UnicodeString & /*appendErrorMessage*/,
278        UErrorCode &status) {
279    if (U_FAILURE(status)) {
280        return FALSE;
281    }
282    return TRUE;
283}
284
285UBool DataDrivenNumberFormatTestSuite::isParsePass(
286        const NumberFormatTestTuple & /* tuple */,
287        UnicodeString & /*appendErrorMessage*/,
288        UErrorCode &status) {
289    if (U_FAILURE(status)) {
290        return FALSE;
291    }
292    return TRUE;
293}
294
295UBool DataDrivenNumberFormatTestSuite::isParseCurrencyPass(
296        const NumberFormatTestTuple & /* tuple */,
297        UnicodeString & /*appendErrorMessage*/,
298        UErrorCode &status) {
299    if (U_FAILURE(status)) {
300        return FALSE;
301    }
302    return TRUE;
303}
304
305UBool DataDrivenNumberFormatTestSuite::isSelectPass(
306        const NumberFormatTestTuple & /* tuple */,
307        UnicodeString & /*appendErrorMessage*/,
308        UErrorCode &status) {
309    if (U_FAILURE(status)) {
310        return FALSE;
311    }
312    return TRUE;
313}
314#endif /* !UCONFIG_NO_FORMATTING */
315