1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "Test.h"
9 
10 #if defined(SK_XML)
11 #include "SkPEG.h"
12 
13 using namespace skpeg;
14 
15 namespace {
16 
17 struct Alpha {
18     using V = char;
19     using MatchT = MatchResult<V>;
20 
21     static MatchT Match(const char* in) {
22         static constexpr unsigned kAlphaRange = 'z' - 'a';
23         return static_cast<unsigned>(*in - 'a') <= kAlphaRange
24             || static_cast<unsigned>(*in - 'A') <= kAlphaRange
25             ? MatchT(in + 1, *in)
26             : nullptr;
27     }
28 };
29 
30 struct Digit {
31     using V = uint8_t;
32     using MatchT = MatchResult<V>;
33 
34     static MatchT Match(const char* in) {
35         static constexpr unsigned kDigitRange = '9' - '0';
36         return static_cast<unsigned>(*in - '0') <= kDigitRange
37             ? MatchT(in + 1, SkTo<uint8_t>(*in - '0'))
38             : nullptr;
39     }
40 };
41 
42 void test_EOS(skiatest::Reporter* r) {
43     static const struct {
44         const char* fInput;
45         bool        fMatch;
46     } gTests[] = {
47         { ""   , true  },
48         { " "  , false },
49         { "\0" , true  },
50         { "foo", false },
51     };
52 
53     for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
54         const auto match = EOS::Match(gTests[i].fInput);
55         REPORTER_ASSERT(r, match == gTests[i].fMatch);
56         REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput : nullptr));
57     }
58 }
59 
60 void test_LIT(skiatest::Reporter* r) {
61     static const struct {
62         const char* fInput;
63         bool        fMatch;
64     } gTests[] = {
65         { ""  , false },
66         { " " , false },
67         { "x" , false },
68         { "X" , true  },
69         { "xX", false },
70         { "Xx", true  },
71     };
72 
73     for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
74         const auto match = LIT<'X'>::Match(gTests[i].fInput);
75         REPORTER_ASSERT(r, match == gTests[i].fMatch);
76         REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput + 1 : nullptr));
77     }
78 
79     REPORTER_ASSERT(r, !(LIT<'F', 'o', 'o'>::Match("")));
80     REPORTER_ASSERT(r, !(LIT<'F', 'o', 'o'>::Match("Fo")));
81     REPORTER_ASSERT(r, !(LIT<'F', 'o', 'o'>::Match("FoO")));
82     REPORTER_ASSERT(r,  (LIT<'F', 'o', 'o'>::Match("Foo")));
83     REPORTER_ASSERT(r,  (LIT<'F', 'o', 'o'>::Match("Foobar")));
84 }
85 
86 void test_Alpha(skiatest::Reporter* r) {
87     static const struct {
88         const char* fInput;
89         bool        fMatch;
90         char        fMatchValue;
91     } gTests[] = {
92         { ""  , false,  0  },
93         { "\r", false,  0  },
94         { "\n", false,  0  },
95         { "\t", false,  0  },
96         { "0" , false,  0  },
97         { "9" , false,  0  },
98         { "a" , true , 'a' },
99         { "a" , true , 'a' },
100         { "z" , true , 'z' },
101         { "A" , true , 'A' },
102         { "Z" , true , 'Z' },
103         { "az", true , 'a' },
104         { "a0", true , 'a' },
105         { "0a", false,  0  },
106     };
107 
108     for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
109         const auto match = Alpha::Match(gTests[i].fInput);
110         REPORTER_ASSERT(r, match == gTests[i].fMatch);
111         REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput + 1 : nullptr));
112         if (match) {
113             REPORTER_ASSERT(r, *match == gTests[i].fMatchValue);
114         }
115     }
116 }
117 
118 void test_Digit(skiatest::Reporter* r) {
119     static const struct {
120         const char* fInput;
121         bool        fMatch;
122         uint8_t     fMatchValue;
123     } gTests[] = {
124         { ""   , false, 0 },
125         { "/"  , false, 0 },
126         { ":"  , false, 0 },
127         { "x"  , false, 0 },
128         { "x0" , false, 0 },
129         { "0"  , true , 0 },
130         { "1x" , true , 1 },
131         { "9 a", true , 9 },
132     };
133 
134     for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
135         const auto match = Digit::Match(gTests[i].fInput);
136         REPORTER_ASSERT(r, match == gTests[i].fMatch);
137         REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput + 1 : nullptr));
138         if (match) {
139             REPORTER_ASSERT(r, *match == gTests[i].fMatchValue);
140         }
141     }
142 }
143 
144 void test_Opt(skiatest::Reporter* r) {
145     static const struct {
146         const char* fInput;
147         bool        fMatch;
148     } gTests[] = {
149         { ""       , false },
150         { "fo"     , false },
151         { " foo"   , false },
152         { "foo"    , true },
153         { "foobar" , true },
154     };
155 
156     for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
157         const auto m = Opt<LIT<'f', 'o', 'o'>>::Match(gTests[i].fInput);
158         REPORTER_ASSERT(r, m);
159         REPORTER_ASSERT(r, m->fValue.isValid() == gTests[i].fMatch);
160     }
161 }
162 
163 void test_Seq(skiatest::Reporter* r) {
164     REPORTER_ASSERT(r,  (Seq<LIT<'X'>, EOS>::Match("X")));
165     REPORTER_ASSERT(r, !(Seq<LIT<'X'>, EOS>::Match("x")));
166     REPORTER_ASSERT(r, !(Seq<LIT<'X'>, EOS>::Match("xX")));
167     REPORTER_ASSERT(r, !(Seq<LIT<'X'>, EOS>::Match("XX")));
168     REPORTER_ASSERT(r,  (Seq<LIT<'X'>, Seq<LIT<'X'>, EOS>>::Match("XX")));
169     REPORTER_ASSERT(r,  (Seq<LIT<'X'>, Seq<LIT<'X'>, EOS>>::Match("XX")));
170 
171     REPORTER_ASSERT(r, !(Seq<LIT<'F', 'o', 'o'>, EOS>::Match("FooBar")));
172     REPORTER_ASSERT(r,  (Seq<LIT<'F', 'o', 'o'>, EOS>::Match("Foo")));
173 
174     {
175         const auto m = Seq<LIT<'x'>, Digit>::Match("x5");
176         REPORTER_ASSERT(r, m);
177         REPORTER_ASSERT(r, m->get<1>() == 5);
178     }
179     {
180         const auto m = Seq<Digit, Digit>::Match("42");
181         REPORTER_ASSERT(r, m);
182         REPORTER_ASSERT(r, m->get<0>() == 4);
183         REPORTER_ASSERT(r, m->get<1>() == 2);
184     }
185 }
186 
187 void test_Choice(skiatest::Reporter* r) {
188     REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match("")));
189     REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match("\t")));
190     REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(" ")));
191     REPORTER_ASSERT(r,  (Choice<Digit,Alpha>::Match("a")));
192     REPORTER_ASSERT(r,  (Choice<Digit,Alpha>::Match("3")));
193     REPORTER_ASSERT(r,  (Choice<Digit,Alpha>::Match("a ")));
194     REPORTER_ASSERT(r,  (Choice<Digit,Alpha>::Match("3 ")));
195     REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(" a ")));
196     REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(" 3 ")));
197 
198     {
199         const auto m = Choice<Alpha, Digit>::Match("x");
200         REPORTER_ASSERT(r,  m);
201         REPORTER_ASSERT(r,  m->v1.isValid());
202         REPORTER_ASSERT(r, !m->v2.isValid());
203         REPORTER_ASSERT(r, *m->v1.get() == 'x');
204     }
205 
206     {
207         const auto m = Choice<Alpha, Digit>::Match("7");
208         REPORTER_ASSERT(r,  m);
209         REPORTER_ASSERT(r, !m->v1.isValid());
210         REPORTER_ASSERT(r,  m->v2.isValid());
211         REPORTER_ASSERT(r, *m->v2.get() == 7);
212     }
213 }
214 
215 void test_AnySome(skiatest::Reporter* r) {
216     static const struct {
217         const char* fInput;
218         int         fCount;
219     } gTests[] = {
220         { ""      , 0 },
221         { "fo"    , 0 },
222         { "Foo"   , 0 },
223         { "foo"   , 1 },
224         { "foofoo", 2 },
225     };
226 
227     for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
228         const auto matchAny = Any<LIT<'f', 'o', 'o'>>::Match(gTests[i].fInput);
229         REPORTER_ASSERT(r, matchAny);
230         REPORTER_ASSERT(r, matchAny->fValues.count() == gTests[i].fCount);
231 
232         const auto matchSome = Some<LIT<'f', 'o', 'o'>>::Match(gTests[i].fInput);
233         REPORTER_ASSERT(r,  matchSome == (gTests[i].fCount > 0));
234         REPORTER_ASSERT(r, !matchSome ||
235                             matchSome->get<1>().fValues.count() == gTests[i].fCount - 1);
236     }
237 
238     {
239         const auto m = Any<Digit>::Match("0123456789foo");
240         REPORTER_ASSERT(r, m);
241         REPORTER_ASSERT(r, m->fValues.count() == 10);
242         for (int i = 0; i < m->fValues.count(); ++i) {
243             REPORTER_ASSERT(r, m->fValues[i] == i);
244         }
245     }
246 }
247 
248 void test_Complex(skiatest::Reporter* r) {
249     // [0-9]+(,[0-9]+)?$
250     using P0 =
251         Seq<
252           Some<Digit>,
253           Opt<Seq<
254             LIT<','>,
255             Some<Digit>>>,
256           EOS>;
257 
258     REPORTER_ASSERT(r, !P0::Match(""));
259     REPORTER_ASSERT(r, !P0::Match(","));
260     REPORTER_ASSERT(r, !P0::Match("1,"));
261     REPORTER_ASSERT(r, !P0::Match(",1"));
262     REPORTER_ASSERT(r,  P0::Match("1"));
263     REPORTER_ASSERT(r,  P0::Match("1,2"));
264     REPORTER_ASSERT(r, !P0::Match("1,2 "));
265     REPORTER_ASSERT(r,  P0::Match("123,456"));
266 
267     // [ ]*[Ff]oo([Bb]ar)+[Bb]az[ ]*$
268     using P1 =
269         Seq<
270           Any<LIT<' '>>,
271           Choice<LIT<'F'>, LIT<'f'>>,
272           LIT<'o', 'o'>,
273           Some<Seq<
274             Choice<LIT<'B'>, LIT<'b'>>,
275             LIT<'a', 'r'>>>,
276           Choice<LIT<'B'>, LIT<'b'>>,
277           LIT<'a', 'z'>,
278           Any<LIT<' '>>,
279           EOS>;
280 
281     REPORTER_ASSERT(r, !P1::Match(""));
282     REPORTER_ASSERT(r, !P1::Match("FooBar"));
283     REPORTER_ASSERT(r, !P1::Match("FooBaz"));
284     REPORTER_ASSERT(r,  P1::Match("FooBarBaz"));
285     REPORTER_ASSERT(r,  P1::Match("foobarbaz"));
286     REPORTER_ASSERT(r,  P1::Match("  FooBarbaz     "));
287     REPORTER_ASSERT(r,  P1::Match(" FooBarbarbarBaz "));
288 }
289 
290 } // anonymous ns
291 
292 DEF_TEST(SkPEG, r) {
293     test_EOS(r);
294     test_LIT(r);
295     test_Alpha(r);
296     test_Digit(r);
297     test_Opt(r);
298     test_Seq(r);
299     test_Choice(r);
300     test_AnySome(r);
301     test_Complex(r);
302 }
303 
304 #endif // SK_XML
305