1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "config.h"
6#include "core/css/parser/MediaQueryTokenizer.h"
7
8#include "core/css/parser/MediaQueryBlockWatcher.h"
9#include "wtf/PassOwnPtr.h"
10#include <gtest/gtest.h>
11
12namespace blink {
13
14typedef struct {
15    const char* input;
16    const char* output;
17} TestCase;
18
19typedef struct {
20    const char* input;
21    const unsigned maxLevel;
22    const unsigned finalLevel;
23} BlockTestCase;
24
25TEST(MediaQueryTokenizerTest, Basic)
26{
27    TestCase testCases[] = {
28        { "(max-width: 50px)", "(max-width: 50px)" },
29        { "(max-width: 1e+2px)", "(max-width: 100px)" },
30        { "(max-width: 1e2px)", "(max-width: 100px)" },
31        { "(max-width: 1000e-1px)", "(max-width: 100px)" },
32        { "(max-width: 50\\70\\78)", "(max-width: 50px)" },
33        { "(max-width: /* comment */50px)", "(max-width: 50px)" },
34        { "(max-width: /** *commen*t */60px)", "(max-width: 60px)" },
35        { "(max-width: /** *commen*t **/70px)", "(max-width: 70px)" },
36        { "(max-width: /** *commen*t **//**/80px)", "(max-width: 80px)" },
37        { "(max-width: /*/ **/90px)", "(max-width: 90px)" },
38        { "(max-width: /*/ **/*100px)", "(max-width: '*'100px)" },
39        { "(max-width: 110px/*)", "(max-width: 110px" },
40        { "(max-width: 120px)/*", "(max-width: 120px)" },
41        { "(max-width: 130px)/**", "(max-width: 130px)" },
42        { "(max-width: /***/140px)/**/", "(max-width: 140px)" },
43        { "(max-width: '40px')", "(max-width: 40px)" },
44        { "(max-width: '40px", "(max-width: 40px" },
45        { "(max-width: '40px\n", "(max-width:  " },
46        { "(max-width: '40px\\", "(max-width: 40px" },
47        { "(max-width: '40px\\\n", "(max-width: 40px" },
48        { "(max-width: '40px\\\n')", "(max-width: 40px)" },
49        { "(max-width: '40\\70\\78')", "(max-width: 40px)" },
50        { "(max-width: '40\\\npx')", "(max-width: 40px)" },
51        { "(max-aspect-ratio: 5)", "(max-aspect-ratio: 5)" },
52        { "(max-aspect-ratio: +5)", "(max-aspect-ratio: 5)" },
53        { "(max-aspect-ratio: -5)", "(max-aspect-ratio: -5)" },
54        { "(max-aspect-ratio: -+5)", "(max-aspect-ratio: '-'5)" },
55        { "(max-aspect-ratio: +-5)", "(max-aspect-ratio: '+'-5)" },
56        { "(max-aspect-ratio: +bla5)", "(max-aspect-ratio: '+'bla5)" },
57        { "(max-aspect-ratio: +5bla)", "(max-aspect-ratio: 5other)" },
58        { "(max-aspect-ratio: -bla)", "(max-aspect-ratio: -bla)" },
59        { "(max-aspect-ratio: --bla)", "(max-aspect-ratio: '-'-bla)" },
60        { 0, 0 } // Do not remove the terminator line.
61    };
62
63    for (int i = 0; testCases[i].input; ++i) {
64        Vector<MediaQueryToken> tokens;
65        MediaQueryTokenizer::tokenize(testCases[i].input, tokens);
66        StringBuilder output;
67        for (size_t j = 0; j < tokens.size(); ++j)
68            output.append(tokens[j].textForUnitTests());
69        ASSERT_STREQ(testCases[i].output, output.toString().ascii().data());
70    }
71}
72
73TEST(MediaQueryTokenizerBlockTest, Basic)
74{
75    BlockTestCase testCases[] = {
76        {"(max-width: 800px()), (max-width: 800px)", 2, 0},
77        {"(max-width: 900px(()), (max-width: 900px)", 3, 1},
78        {"(max-width: 600px(())))), (max-width: 600px)", 3, 0},
79        {"(max-width: 500px(((((((((())))), (max-width: 500px)", 11, 6},
80        {"(max-width: 800px[]), (max-width: 800px)", 2, 0},
81        {"(max-width: 900px[[]), (max-width: 900px)", 3, 2},
82        {"(max-width: 600px[[]]]]), (max-width: 600px)", 3, 0},
83        {"(max-width: 500px[[[[[[[[[[]]]]), (max-width: 500px)", 11, 7},
84        {"(max-width: 800px{}), (max-width: 800px)", 2, 0},
85        {"(max-width: 900px{{}), (max-width: 900px)", 3, 2},
86        {"(max-width: 600px{{}}}}), (max-width: 600px)", 3, 0},
87        {"(max-width: 500px{{{{{{{{{{}}}}), (max-width: 500px)", 11, 7},
88        {"[(), (max-width: 400px)", 2, 1},
89        {"[{}, (max-width: 500px)", 2, 1},
90        {"[{]}], (max-width: 900px)", 2, 0},
91        {"[{[]{}{{{}}}}], (max-width: 900px)", 5, 0},
92        {"[{[}], (max-width: 900px)", 3, 2},
93        {"[({)}], (max-width: 900px)", 3, 2},
94        {"[]((), (max-width: 900px)", 2, 1},
95        {"((), (max-width: 900px)", 2, 1},
96        {"(foo(), (max-width: 900px)", 2, 1},
97        {"[](()), (max-width: 900px)", 2, 0},
98        {"all an[isdfs bla())(i())]icalc(i)(()), (max-width: 400px)", 3, 0},
99        {"all an[isdfs bla())(]icalc(i)(()), (max-width: 500px)", 4, 2},
100        {"all an[isdfs bla())(]icalc(i)(())), (max-width: 600px)", 4, 1},
101        {"all an[isdfs bla())(]icalc(i)(()))], (max-width: 800px)", 4, 0},
102        {0, 0, 0} // Do not remove the terminator line.
103    };
104    for (int i = 0; testCases[i].input; ++i) {
105        Vector<MediaQueryToken> tokens;
106        MediaQueryTokenizer::tokenize(testCases[i].input, tokens);
107        MediaQueryBlockWatcher blockWatcher;
108
109        unsigned maxLevel = 0;
110        unsigned level = 0;
111        for (size_t j = 0; j < tokens.size(); ++j) {
112            blockWatcher.handleToken(tokens[j]);
113            level = blockWatcher.blockLevel();
114            maxLevel = std::max(level, maxLevel);
115        }
116        ASSERT_EQ(testCases[i].maxLevel, maxLevel);
117        ASSERT_EQ(testCases[i].finalLevel, level);
118    }
119}
120
121void testToken(UChar c, MediaQueryTokenType tokenType)
122{
123    Vector<MediaQueryToken> tokens;
124    StringBuilder input;
125    input.append(c);
126    MediaQueryTokenizer::tokenize(input.toString(), tokens);
127    ASSERT_EQ(tokens[0].type(), tokenType);
128}
129
130TEST(MediaQueryTokenizerCodepointsTest, Basic)
131{
132    for (UChar c = 0; c <= 1000; ++c) {
133        if (isASCIIDigit(c))
134            testToken(c, NumberToken);
135        else if (isASCIIAlpha(c))
136            testToken(c, IdentToken);
137        else if (c == '_')
138            testToken(c, IdentToken);
139        else if (c == '\r' || c == ' ' || c == '\n' || c == '\t' || c == '\f')
140            testToken(c, WhitespaceToken);
141        else if (c == '(')
142            testToken(c, LeftParenthesisToken);
143        else if (c == ')')
144            testToken(c, RightParenthesisToken);
145        else if (c == '[')
146            testToken(c, LeftBracketToken);
147        else if (c == ']')
148            testToken(c, RightBracketToken);
149        else if (c == '{')
150            testToken(c, LeftBraceToken);
151        else if (c == '}')
152            testToken(c, RightBraceToken);
153        else if (c == '.' || c == '+' || c == '-' || c == '/' || c == '\\')
154            testToken(c, DelimiterToken);
155        else if (c == '\'' || c == '"')
156            testToken(c, StringToken);
157        else if (c == ',')
158            testToken(c, CommaToken);
159        else if (c == ':')
160            testToken(c, ColonToken);
161        else if (c == ';')
162            testToken(c, SemicolonToken);
163        else if (!c)
164            testToken(c, EOFToken);
165        else if (c > SCHAR_MAX)
166            testToken(c, IdentToken);
167        else
168            testToken(c, DelimiterToken);
169    }
170    testToken(USHRT_MAX, IdentToken);
171}
172
173} // namespace
174