xref: /aosp_15_r20/external/protobuf/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs (revision 1b3f573f81763fcece89efc2b6a5209149e44ab8)
1*1b3f573fSAndroid Build Coastguard Worker #region Copyright notice and license
2*1b3f573fSAndroid Build Coastguard Worker // Protocol Buffers - Google's data interchange format
3*1b3f573fSAndroid Build Coastguard Worker // Copyright 2008 Google Inc.  All rights reserved.
4*1b3f573fSAndroid Build Coastguard Worker // https://developers.google.com/protocol-buffers/
5*1b3f573fSAndroid Build Coastguard Worker //
6*1b3f573fSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without
7*1b3f573fSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are
8*1b3f573fSAndroid Build Coastguard Worker // met:
9*1b3f573fSAndroid Build Coastguard Worker //
10*1b3f573fSAndroid Build Coastguard Worker //     * Redistributions of source code must retain the above copyright
11*1b3f573fSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer.
12*1b3f573fSAndroid Build Coastguard Worker //     * Redistributions in binary form must reproduce the above
13*1b3f573fSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer
14*1b3f573fSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the
15*1b3f573fSAndroid Build Coastguard Worker // distribution.
16*1b3f573fSAndroid Build Coastguard Worker //     * Neither the name of Google Inc. nor the names of its
17*1b3f573fSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from
18*1b3f573fSAndroid Build Coastguard Worker // this software without specific prior written permission.
19*1b3f573fSAndroid Build Coastguard Worker //
20*1b3f573fSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21*1b3f573fSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23*1b3f573fSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24*1b3f573fSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25*1b3f573fSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27*1b3f573fSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28*1b3f573fSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29*1b3f573fSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30*1b3f573fSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31*1b3f573fSAndroid Build Coastguard Worker #endregion
32*1b3f573fSAndroid Build Coastguard Worker using NUnit.Framework;
33*1b3f573fSAndroid Build Coastguard Worker using System;
34*1b3f573fSAndroid Build Coastguard Worker using System.IO;
35*1b3f573fSAndroid Build Coastguard Worker 
36*1b3f573fSAndroid Build Coastguard Worker namespace Google.Protobuf
37*1b3f573fSAndroid Build Coastguard Worker {
38*1b3f573fSAndroid Build Coastguard Worker     public class JsonTokenizerTest
39*1b3f573fSAndroid Build Coastguard Worker     {
40*1b3f573fSAndroid Build Coastguard Worker         [Test]
EmptyObjectValue()41*1b3f573fSAndroid Build Coastguard Worker         public void EmptyObjectValue()
42*1b3f573fSAndroid Build Coastguard Worker         {
43*1b3f573fSAndroid Build Coastguard Worker             AssertTokens("{}", JsonToken.StartObject, JsonToken.EndObject);
44*1b3f573fSAndroid Build Coastguard Worker         }
45*1b3f573fSAndroid Build Coastguard Worker 
46*1b3f573fSAndroid Build Coastguard Worker         [Test]
EmptyArrayValue()47*1b3f573fSAndroid Build Coastguard Worker         public void EmptyArrayValue()
48*1b3f573fSAndroid Build Coastguard Worker         {
49*1b3f573fSAndroid Build Coastguard Worker             AssertTokens("[]", JsonToken.StartArray, JsonToken.EndArray);
50*1b3f573fSAndroid Build Coastguard Worker         }
51*1b3f573fSAndroid Build Coastguard Worker 
52*1b3f573fSAndroid Build Coastguard Worker         [Test]
53*1b3f573fSAndroid Build Coastguard Worker         [TestCase("foo", "foo")]
54*1b3f573fSAndroid Build Coastguard Worker         [TestCase("tab\\t", "tab\t")]
55*1b3f573fSAndroid Build Coastguard Worker         [TestCase("line\\nfeed", "line\nfeed")]
56*1b3f573fSAndroid Build Coastguard Worker         [TestCase("carriage\\rreturn", "carriage\rreturn")]
57*1b3f573fSAndroid Build Coastguard Worker         [TestCase("back\\bspace", "back\bspace")]
58*1b3f573fSAndroid Build Coastguard Worker         [TestCase("form\\ffeed", "form\ffeed")]
59*1b3f573fSAndroid Build Coastguard Worker         [TestCase("escaped\\/slash", "escaped/slash")]
60*1b3f573fSAndroid Build Coastguard Worker         [TestCase("escaped\\\\backslash", "escaped\\backslash")]
61*1b3f573fSAndroid Build Coastguard Worker         [TestCase("escaped\\\"quote", "escaped\"quote")]
62*1b3f573fSAndroid Build Coastguard Worker         [TestCase("foo {}[] bar", "foo {}[] bar")]
63*1b3f573fSAndroid Build Coastguard Worker         [TestCase("foo\\u09aFbar", "foo\u09afbar")] // Digits, upper hex, lower hex
64*1b3f573fSAndroid Build Coastguard Worker         [TestCase("ab\ud800\udc00cd", "ab\ud800\udc00cd")]
65*1b3f573fSAndroid Build Coastguard Worker         [TestCase("ab\\ud800\\udc00cd", "ab\ud800\udc00cd")]
StringValue(string json, string expectedValue)66*1b3f573fSAndroid Build Coastguard Worker         public void StringValue(string json, string expectedValue)
67*1b3f573fSAndroid Build Coastguard Worker         {
68*1b3f573fSAndroid Build Coastguard Worker             AssertTokensNoReplacement("\"" + json + "\"", JsonToken.Value(expectedValue));
69*1b3f573fSAndroid Build Coastguard Worker         }
70*1b3f573fSAndroid Build Coastguard Worker 
71*1b3f573fSAndroid Build Coastguard Worker         // Valid surrogate pairs, with mixed escaping. These test cases can't be expressed
72*1b3f573fSAndroid Build Coastguard Worker         // using TestCase as they have no valid UTF-8 representation.
73*1b3f573fSAndroid Build Coastguard Worker         // It's unclear exactly how we should handle a mixture of escaped or not: that can't
74*1b3f573fSAndroid Build Coastguard Worker         // come from UTF-8 text, but could come from a .NET string. For the moment,
75*1b3f573fSAndroid Build Coastguard Worker         // treat it as valid in the obvious way.
76*1b3f573fSAndroid Build Coastguard Worker         [Test]
MixedSurrogatePairs()77*1b3f573fSAndroid Build Coastguard Worker         public void MixedSurrogatePairs()
78*1b3f573fSAndroid Build Coastguard Worker         {
79*1b3f573fSAndroid Build Coastguard Worker             string expected = "\ud800\udc00";
80*1b3f573fSAndroid Build Coastguard Worker             AssertTokens("'\\ud800\udc00'", JsonToken.Value(expected));
81*1b3f573fSAndroid Build Coastguard Worker             AssertTokens("'\ud800\\udc00'", JsonToken.Value(expected));
82*1b3f573fSAndroid Build Coastguard Worker         }
83*1b3f573fSAndroid Build Coastguard Worker 
84*1b3f573fSAndroid Build Coastguard Worker         [Test]
ObjectDepth()85*1b3f573fSAndroid Build Coastguard Worker         public void ObjectDepth()
86*1b3f573fSAndroid Build Coastguard Worker         {
87*1b3f573fSAndroid Build Coastguard Worker             string json = "{ \"foo\": { \"x\": 1, \"y\": [ 0 ] } }";
88*1b3f573fSAndroid Build Coastguard Worker             var tokenizer = JsonTokenizer.FromTextReader(new StringReader(json));
89*1b3f573fSAndroid Build Coastguard Worker             // If we had more tests like this, I'd introduce a helper method... but for one test, it's not worth it.
90*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(0, tokenizer.ObjectDepth);
91*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.StartObject, tokenizer.Next());
92*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(1, tokenizer.ObjectDepth);
93*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.Name("foo"), tokenizer.Next());
94*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(1, tokenizer.ObjectDepth);
95*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.StartObject, tokenizer.Next());
96*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(2, tokenizer.ObjectDepth);
97*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.Name("x"), tokenizer.Next());
98*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(2, tokenizer.ObjectDepth);
99*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.Value(1), tokenizer.Next());
100*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(2, tokenizer.ObjectDepth);
101*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.Name("y"), tokenizer.Next());
102*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(2, tokenizer.ObjectDepth);
103*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.StartArray, tokenizer.Next());
104*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(2, tokenizer.ObjectDepth); // Depth hasn't changed in array
105*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.Value(0), tokenizer.Next());
106*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(2, tokenizer.ObjectDepth);
107*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.EndArray, tokenizer.Next());
108*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(2, tokenizer.ObjectDepth);
109*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.EndObject, tokenizer.Next());
110*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(1, tokenizer.ObjectDepth);
111*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.EndObject, tokenizer.Next());
112*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(0, tokenizer.ObjectDepth);
113*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.EndDocument, tokenizer.Next());
114*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(0, tokenizer.ObjectDepth);
115*1b3f573fSAndroid Build Coastguard Worker         }
116*1b3f573fSAndroid Build Coastguard Worker 
117*1b3f573fSAndroid Build Coastguard Worker         [Test]
ObjectDepth_WithPushBack()118*1b3f573fSAndroid Build Coastguard Worker         public void ObjectDepth_WithPushBack()
119*1b3f573fSAndroid Build Coastguard Worker         {
120*1b3f573fSAndroid Build Coastguard Worker             string json = "{}";
121*1b3f573fSAndroid Build Coastguard Worker             var tokenizer = JsonTokenizer.FromTextReader(new StringReader(json));
122*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(0, tokenizer.ObjectDepth);
123*1b3f573fSAndroid Build Coastguard Worker             var token = tokenizer.Next();
124*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(1, tokenizer.ObjectDepth);
125*1b3f573fSAndroid Build Coastguard Worker             // When we push back a "start object", we should effectively be back to the previous depth.
126*1b3f573fSAndroid Build Coastguard Worker             tokenizer.PushBack(token);
127*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(0, tokenizer.ObjectDepth);
128*1b3f573fSAndroid Build Coastguard Worker             // Read the same token again, and get back to depth 1
129*1b3f573fSAndroid Build Coastguard Worker             token = tokenizer.Next();
130*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(1, tokenizer.ObjectDepth);
131*1b3f573fSAndroid Build Coastguard Worker 
132*1b3f573fSAndroid Build Coastguard Worker             // Now the same in reverse, with EndObject
133*1b3f573fSAndroid Build Coastguard Worker             token = tokenizer.Next();
134*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(0, tokenizer.ObjectDepth);
135*1b3f573fSAndroid Build Coastguard Worker             tokenizer.PushBack(token);
136*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(1, tokenizer.ObjectDepth);
137*1b3f573fSAndroid Build Coastguard Worker             tokenizer.Next();
138*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(0, tokenizer.ObjectDepth);
139*1b3f573fSAndroid Build Coastguard Worker         }
140*1b3f573fSAndroid Build Coastguard Worker 
141*1b3f573fSAndroid Build Coastguard Worker         [Test]
142*1b3f573fSAndroid Build Coastguard Worker         [TestCase("embedded tab\t")]
143*1b3f573fSAndroid Build Coastguard Worker         [TestCase("embedded CR\r")]
144*1b3f573fSAndroid Build Coastguard Worker         [TestCase("embedded LF\n")]
145*1b3f573fSAndroid Build Coastguard Worker         [TestCase("embedded bell\u0007")]
146*1b3f573fSAndroid Build Coastguard Worker         [TestCase("bad escape\\a")]
147*1b3f573fSAndroid Build Coastguard Worker         [TestCase("incomplete escape\\")]
148*1b3f573fSAndroid Build Coastguard Worker         [TestCase("incomplete Unicode escape\\u000")]
149*1b3f573fSAndroid Build Coastguard Worker         [TestCase("invalid Unicode escape\\u000H")]
150*1b3f573fSAndroid Build Coastguard Worker         // Surrogate pair handling, both in raw .NET strings and escaped. We only need
151*1b3f573fSAndroid Build Coastguard Worker         // to detect this in strings, as non-ASCII characters anywhere other than in strings
152*1b3f573fSAndroid Build Coastguard Worker         // will already lead to parsing errors.
153*1b3f573fSAndroid Build Coastguard Worker         [TestCase("\\ud800")]
154*1b3f573fSAndroid Build Coastguard Worker         [TestCase("\\udc00")]
155*1b3f573fSAndroid Build Coastguard Worker         [TestCase("\\ud800x")]
156*1b3f573fSAndroid Build Coastguard Worker         [TestCase("\\udc00x")]
157*1b3f573fSAndroid Build Coastguard Worker         [TestCase("\\udc00\\ud800y")]
InvalidStringValue(string json)158*1b3f573fSAndroid Build Coastguard Worker         public void InvalidStringValue(string json)
159*1b3f573fSAndroid Build Coastguard Worker         {
160*1b3f573fSAndroid Build Coastguard Worker             AssertThrowsAfter("\"" + json + "\"");
161*1b3f573fSAndroid Build Coastguard Worker         }
162*1b3f573fSAndroid Build Coastguard Worker 
163*1b3f573fSAndroid Build Coastguard Worker         // Tests for invalid strings that can't be expressed in attributes,
164*1b3f573fSAndroid Build Coastguard Worker         // as the constants can't be expressed as UTF-8 strings.
165*1b3f573fSAndroid Build Coastguard Worker         [Test]
InvalidSurrogatePairs()166*1b3f573fSAndroid Build Coastguard Worker         public void InvalidSurrogatePairs()
167*1b3f573fSAndroid Build Coastguard Worker         {
168*1b3f573fSAndroid Build Coastguard Worker             AssertThrowsAfter("\"\ud800x\"");
169*1b3f573fSAndroid Build Coastguard Worker             AssertThrowsAfter("\"\udc00y\"");
170*1b3f573fSAndroid Build Coastguard Worker             AssertThrowsAfter("\"\udc00\ud800y\"");
171*1b3f573fSAndroid Build Coastguard Worker         }
172*1b3f573fSAndroid Build Coastguard Worker 
173*1b3f573fSAndroid Build Coastguard Worker         [Test]
174*1b3f573fSAndroid Build Coastguard Worker         [TestCase("0", 0)]
175*1b3f573fSAndroid Build Coastguard Worker         [TestCase("-0", 0)] // We don't distinguish between positive and negative 0
176*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1", 1)]
177*1b3f573fSAndroid Build Coastguard Worker         [TestCase("-1", -1)]
178*1b3f573fSAndroid Build Coastguard Worker         // From here on, assume leading sign is okay...
179*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1.125", 1.125)]
180*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1.0", 1)]
181*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1e5", 100000)]
182*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1e000000", 1)] // Weird, but not prohibited by the spec
183*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1E5", 100000)]
184*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1e+5", 100000)]
185*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1E-5", 0.00001)]
186*1b3f573fSAndroid Build Coastguard Worker         [TestCase("123E-2", 1.23)]
187*1b3f573fSAndroid Build Coastguard Worker         [TestCase("123.45E3", 123450)]
188*1b3f573fSAndroid Build Coastguard Worker         [TestCase("   1   ", 1)]
NumberValue(string json, double expectedValue)189*1b3f573fSAndroid Build Coastguard Worker         public void NumberValue(string json, double expectedValue)
190*1b3f573fSAndroid Build Coastguard Worker         {
191*1b3f573fSAndroid Build Coastguard Worker             AssertTokens(json, JsonToken.Value(expectedValue));
192*1b3f573fSAndroid Build Coastguard Worker         }
193*1b3f573fSAndroid Build Coastguard Worker 
194*1b3f573fSAndroid Build Coastguard Worker         [Test]
195*1b3f573fSAndroid Build Coastguard Worker         [TestCase("00")]
196*1b3f573fSAndroid Build Coastguard Worker         [TestCase(".5")]
197*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1.")]
198*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1e")]
199*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1e-")]
200*1b3f573fSAndroid Build Coastguard Worker         [TestCase("--")]
201*1b3f573fSAndroid Build Coastguard Worker         [TestCase("--1")]
202*1b3f573fSAndroid Build Coastguard Worker         [TestCase("-1.7977e308")]
203*1b3f573fSAndroid Build Coastguard Worker         [TestCase("1.7977e308")]
InvalidNumberValue(string json)204*1b3f573fSAndroid Build Coastguard Worker         public void InvalidNumberValue(string json)
205*1b3f573fSAndroid Build Coastguard Worker         {
206*1b3f573fSAndroid Build Coastguard Worker             AssertThrowsAfter(json);
207*1b3f573fSAndroid Build Coastguard Worker         }
208*1b3f573fSAndroid Build Coastguard Worker 
209*1b3f573fSAndroid Build Coastguard Worker         [Test]
210*1b3f573fSAndroid Build Coastguard Worker         [TestCase("nul")]
211*1b3f573fSAndroid Build Coastguard Worker         [TestCase("nothing")]
212*1b3f573fSAndroid Build Coastguard Worker         [TestCase("truth")]
213*1b3f573fSAndroid Build Coastguard Worker         [TestCase("fALSEhood")]
InvalidLiterals(string json)214*1b3f573fSAndroid Build Coastguard Worker         public void InvalidLiterals(string json)
215*1b3f573fSAndroid Build Coastguard Worker         {
216*1b3f573fSAndroid Build Coastguard Worker             AssertThrowsAfter(json);
217*1b3f573fSAndroid Build Coastguard Worker         }
218*1b3f573fSAndroid Build Coastguard Worker 
219*1b3f573fSAndroid Build Coastguard Worker         [Test]
NullValue()220*1b3f573fSAndroid Build Coastguard Worker         public void NullValue()
221*1b3f573fSAndroid Build Coastguard Worker         {
222*1b3f573fSAndroid Build Coastguard Worker             AssertTokens("null", JsonToken.Null);
223*1b3f573fSAndroid Build Coastguard Worker         }
224*1b3f573fSAndroid Build Coastguard Worker 
225*1b3f573fSAndroid Build Coastguard Worker         [Test]
TrueValue()226*1b3f573fSAndroid Build Coastguard Worker         public void TrueValue()
227*1b3f573fSAndroid Build Coastguard Worker         {
228*1b3f573fSAndroid Build Coastguard Worker             AssertTokens("true", JsonToken.True);
229*1b3f573fSAndroid Build Coastguard Worker         }
230*1b3f573fSAndroid Build Coastguard Worker 
231*1b3f573fSAndroid Build Coastguard Worker         [Test]
FalseValue()232*1b3f573fSAndroid Build Coastguard Worker         public void FalseValue()
233*1b3f573fSAndroid Build Coastguard Worker         {
234*1b3f573fSAndroid Build Coastguard Worker             AssertTokens("false", JsonToken.False);
235*1b3f573fSAndroid Build Coastguard Worker         }
236*1b3f573fSAndroid Build Coastguard Worker 
237*1b3f573fSAndroid Build Coastguard Worker         [Test]
SimpleObject()238*1b3f573fSAndroid Build Coastguard Worker         public void SimpleObject()
239*1b3f573fSAndroid Build Coastguard Worker         {
240*1b3f573fSAndroid Build Coastguard Worker             AssertTokens("{'x': 'y'}",
241*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.StartObject, JsonToken.Name("x"), JsonToken.Value("y"), JsonToken.EndObject);
242*1b3f573fSAndroid Build Coastguard Worker         }
243*1b3f573fSAndroid Build Coastguard Worker 
244*1b3f573fSAndroid Build Coastguard Worker         [Test]
245*1b3f573fSAndroid Build Coastguard Worker         [TestCase("[10, 20", 3)]
246*1b3f573fSAndroid Build Coastguard Worker         [TestCase("[10,", 2)]
247*1b3f573fSAndroid Build Coastguard Worker         [TestCase("[10:20]", 2)]
248*1b3f573fSAndroid Build Coastguard Worker         [TestCase("[", 1)]
249*1b3f573fSAndroid Build Coastguard Worker         [TestCase("[,", 1)]
250*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{", 1)]
251*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{,", 1)]
252*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{[", 1)]
253*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{{", 1)]
254*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{0", 1)]
255*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{null", 1)]
256*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{false", 1)]
257*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{true", 1)]
258*1b3f573fSAndroid Build Coastguard Worker         [TestCase("}", 0)]
259*1b3f573fSAndroid Build Coastguard Worker         [TestCase("]", 0)]
260*1b3f573fSAndroid Build Coastguard Worker         [TestCase(",", 0)]
261*1b3f573fSAndroid Build Coastguard Worker         [TestCase("'foo' 'bar'", 1)]
262*1b3f573fSAndroid Build Coastguard Worker         [TestCase(":", 0)]
263*1b3f573fSAndroid Build Coastguard Worker         [TestCase("'foo", 0)] // Incomplete string
264*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ 'foo' }", 2)]
265*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ x:1", 1)] // Property names must be quoted
266*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{]", 1)]
267*1b3f573fSAndroid Build Coastguard Worker         [TestCase("[}", 1)]
268*1b3f573fSAndroid Build Coastguard Worker         [TestCase("[1,", 2)]
269*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{'x':0]", 3)]
270*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ 'foo': }", 2)]
271*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ 'foo':'bar', }", 3)]
InvalidStructure(string json, int expectedValidTokens)272*1b3f573fSAndroid Build Coastguard Worker         public void InvalidStructure(string json, int expectedValidTokens)
273*1b3f573fSAndroid Build Coastguard Worker         {
274*1b3f573fSAndroid Build Coastguard Worker             // Note: we don't test that the earlier tokens are exactly as expected,
275*1b3f573fSAndroid Build Coastguard Worker             // partly because that's hard to parameterize.
276*1b3f573fSAndroid Build Coastguard Worker             var reader = new StringReader(json.Replace('\'', '"'));
277*1b3f573fSAndroid Build Coastguard Worker             var tokenizer = JsonTokenizer.FromTextReader(reader);
278*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < expectedValidTokens; i++)
279*1b3f573fSAndroid Build Coastguard Worker             {
280*1b3f573fSAndroid Build Coastguard Worker                 Assert.IsNotNull(tokenizer.Next());
281*1b3f573fSAndroid Build Coastguard Worker             }
282*1b3f573fSAndroid Build Coastguard Worker             Assert.Throws<InvalidJsonException>(() => tokenizer.Next());
283*1b3f573fSAndroid Build Coastguard Worker         }
284*1b3f573fSAndroid Build Coastguard Worker 
285*1b3f573fSAndroid Build Coastguard Worker         [Test]
ArrayMixedType()286*1b3f573fSAndroid Build Coastguard Worker         public void ArrayMixedType()
287*1b3f573fSAndroid Build Coastguard Worker         {
288*1b3f573fSAndroid Build Coastguard Worker             AssertTokens("[1, 'foo', null, false, true, [2], {'x':'y' }]",
289*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.StartArray,
290*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Value(1),
291*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Value("foo"),
292*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Null,
293*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.False,
294*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.True,
295*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.StartArray,
296*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Value(2),
297*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.EndArray,
298*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.StartObject,
299*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Name("x"),
300*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Value("y"),
301*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.EndObject,
302*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.EndArray);
303*1b3f573fSAndroid Build Coastguard Worker         }
304*1b3f573fSAndroid Build Coastguard Worker 
305*1b3f573fSAndroid Build Coastguard Worker         [Test]
ObjectMixedType()306*1b3f573fSAndroid Build Coastguard Worker         public void ObjectMixedType()
307*1b3f573fSAndroid Build Coastguard Worker         {
308*1b3f573fSAndroid Build Coastguard Worker             AssertTokens(@"{'a': 1, 'b': 'bar', 'c': null, 'd': false, 'e': true,
309*1b3f573fSAndroid Build Coastguard Worker                            'f': [2], 'g': {'x':'y' }}",
310*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.StartObject,
311*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Name("a"),
312*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Value(1),
313*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Name("b"),
314*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Value("bar"),
315*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Name("c"),
316*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Null,
317*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Name("d"),
318*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.False,
319*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Name("e"),
320*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.True,
321*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Name("f"),
322*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.StartArray,
323*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Value(2),
324*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.EndArray,
325*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Name("g"),
326*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.StartObject,
327*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Name("x"),
328*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.Value("y"),
329*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.EndObject,
330*1b3f573fSAndroid Build Coastguard Worker                 JsonToken.EndObject);
331*1b3f573fSAndroid Build Coastguard Worker         }
332*1b3f573fSAndroid Build Coastguard Worker 
333*1b3f573fSAndroid Build Coastguard Worker         [Test]
NextAfterEndDocumentThrows()334*1b3f573fSAndroid Build Coastguard Worker         public void NextAfterEndDocumentThrows()
335*1b3f573fSAndroid Build Coastguard Worker         {
336*1b3f573fSAndroid Build Coastguard Worker             var tokenizer = JsonTokenizer.FromTextReader(new StringReader("null"));
337*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.Null, tokenizer.Next());
338*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.EndDocument, tokenizer.Next());
339*1b3f573fSAndroid Build Coastguard Worker             Assert.Throws<InvalidOperationException>(() => tokenizer.Next());
340*1b3f573fSAndroid Build Coastguard Worker         }
341*1b3f573fSAndroid Build Coastguard Worker 
342*1b3f573fSAndroid Build Coastguard Worker         [Test]
CanPushBackEndDocument()343*1b3f573fSAndroid Build Coastguard Worker         public void CanPushBackEndDocument()
344*1b3f573fSAndroid Build Coastguard Worker         {
345*1b3f573fSAndroid Build Coastguard Worker             var tokenizer = JsonTokenizer.FromTextReader(new StringReader("null"));
346*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.Null, tokenizer.Next());
347*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.EndDocument, tokenizer.Next());
348*1b3f573fSAndroid Build Coastguard Worker             tokenizer.PushBack(JsonToken.EndDocument);
349*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.EndDocument, tokenizer.Next());
350*1b3f573fSAndroid Build Coastguard Worker             Assert.Throws<InvalidOperationException>(() => tokenizer.Next());
351*1b3f573fSAndroid Build Coastguard Worker         }
352*1b3f573fSAndroid Build Coastguard Worker 
353*1b3f573fSAndroid Build Coastguard Worker         [Test]
354*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ 'skip': 0, 'next': 1")]
355*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ 'skip': [0, 1, 2], 'next': 1")]
356*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ 'skip': 'x', 'next': 1")]
357*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ 'skip': ['x', 'y'], 'next': 1")]
358*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ 'skip': {'a': 0}, 'next': 1")]
359*1b3f573fSAndroid Build Coastguard Worker         [TestCase("{ 'skip': {'a': [0, {'b':[]}]}, 'next': 1")]
SkipValue(string json)360*1b3f573fSAndroid Build Coastguard Worker         public void SkipValue(string json)
361*1b3f573fSAndroid Build Coastguard Worker         {
362*1b3f573fSAndroid Build Coastguard Worker             var tokenizer = JsonTokenizer.FromTextReader(new StringReader(json.Replace('\'', '"')));
363*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual(JsonToken.StartObject, tokenizer.Next());
364*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual("skip", tokenizer.Next().StringValue);
365*1b3f573fSAndroid Build Coastguard Worker             tokenizer.SkipValue();
366*1b3f573fSAndroid Build Coastguard Worker             Assert.AreEqual("next", tokenizer.Next().StringValue);
367*1b3f573fSAndroid Build Coastguard Worker         }
368*1b3f573fSAndroid Build Coastguard Worker 
369*1b3f573fSAndroid Build Coastguard Worker         /// <summary>
370*1b3f573fSAndroid Build Coastguard Worker         /// Asserts that the specified JSON is tokenized into the given sequence of tokens.
371*1b3f573fSAndroid Build Coastguard Worker         /// All apostrophes are first converted to double quotes, allowing any tests
372*1b3f573fSAndroid Build Coastguard Worker         /// that don't need to check actual apostrophe handling to use apostrophes in the JSON, avoiding
373*1b3f573fSAndroid Build Coastguard Worker         /// messy string literal escaping. The "end document" token is not specified in the list of
374*1b3f573fSAndroid Build Coastguard Worker         /// expected tokens, but is implicit.
375*1b3f573fSAndroid Build Coastguard Worker         /// </summary>
AssertTokens(string json, params JsonToken[] expectedTokens)376*1b3f573fSAndroid Build Coastguard Worker         private static void AssertTokens(string json, params JsonToken[] expectedTokens)
377*1b3f573fSAndroid Build Coastguard Worker         {
378*1b3f573fSAndroid Build Coastguard Worker             AssertTokensNoReplacement(json.Replace('\'', '"'), expectedTokens);
379*1b3f573fSAndroid Build Coastguard Worker         }
380*1b3f573fSAndroid Build Coastguard Worker 
381*1b3f573fSAndroid Build Coastguard Worker         /// <summary>
382*1b3f573fSAndroid Build Coastguard Worker         /// Asserts that the specified JSON is tokenized into the given sequence of tokens.
383*1b3f573fSAndroid Build Coastguard Worker         /// Unlike <see cref="AssertTokens(string, JsonToken[])"/>, this does not perform any character
384*1b3f573fSAndroid Build Coastguard Worker         /// replacement on the specified JSON, and should be used when the text contains apostrophes which
385*1b3f573fSAndroid Build Coastguard Worker         /// are expected to be used *as* apostrophes. The "end document" token is not specified in the list of
386*1b3f573fSAndroid Build Coastguard Worker         /// expected tokens, but is implicit.
387*1b3f573fSAndroid Build Coastguard Worker         /// </summary>
AssertTokensNoReplacement(string json, params JsonToken[] expectedTokens)388*1b3f573fSAndroid Build Coastguard Worker         private static void AssertTokensNoReplacement(string json, params JsonToken[] expectedTokens)
389*1b3f573fSAndroid Build Coastguard Worker         {
390*1b3f573fSAndroid Build Coastguard Worker             var reader = new StringReader(json);
391*1b3f573fSAndroid Build Coastguard Worker             var tokenizer = JsonTokenizer.FromTextReader(reader);
392*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < expectedTokens.Length; i++)
393*1b3f573fSAndroid Build Coastguard Worker             {
394*1b3f573fSAndroid Build Coastguard Worker                 var actualToken = tokenizer.Next();
395*1b3f573fSAndroid Build Coastguard Worker                 if (actualToken == JsonToken.EndDocument)
396*1b3f573fSAndroid Build Coastguard Worker                 {
397*1b3f573fSAndroid Build Coastguard Worker                     Assert.Fail("Expected {0} but reached end of token stream", expectedTokens[i]);
398*1b3f573fSAndroid Build Coastguard Worker                 }
399*1b3f573fSAndroid Build Coastguard Worker                 Assert.AreEqual(expectedTokens[i], actualToken);
400*1b3f573fSAndroid Build Coastguard Worker             }
401*1b3f573fSAndroid Build Coastguard Worker             var finalToken = tokenizer.Next();
402*1b3f573fSAndroid Build Coastguard Worker             if (finalToken != JsonToken.EndDocument)
403*1b3f573fSAndroid Build Coastguard Worker             {
404*1b3f573fSAndroid Build Coastguard Worker                 Assert.Fail("Expected token stream to be exhausted; received {0}", finalToken);
405*1b3f573fSAndroid Build Coastguard Worker             }
406*1b3f573fSAndroid Build Coastguard Worker         }
407*1b3f573fSAndroid Build Coastguard Worker 
AssertThrowsAfter(string json, params JsonToken[] expectedTokens)408*1b3f573fSAndroid Build Coastguard Worker         private static void AssertThrowsAfter(string json, params JsonToken[] expectedTokens)
409*1b3f573fSAndroid Build Coastguard Worker         {
410*1b3f573fSAndroid Build Coastguard Worker             var reader = new StringReader(json);
411*1b3f573fSAndroid Build Coastguard Worker             var tokenizer = JsonTokenizer.FromTextReader(reader);
412*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < expectedTokens.Length; i++)
413*1b3f573fSAndroid Build Coastguard Worker             {
414*1b3f573fSAndroid Build Coastguard Worker                 var actualToken = tokenizer.Next();
415*1b3f573fSAndroid Build Coastguard Worker                 if (actualToken == JsonToken.EndDocument)
416*1b3f573fSAndroid Build Coastguard Worker                 {
417*1b3f573fSAndroid Build Coastguard Worker                     Assert.Fail("Expected {0} but reached end of document", expectedTokens[i]);
418*1b3f573fSAndroid Build Coastguard Worker                 }
419*1b3f573fSAndroid Build Coastguard Worker                 Assert.AreEqual(expectedTokens[i], actualToken);
420*1b3f573fSAndroid Build Coastguard Worker             }
421*1b3f573fSAndroid Build Coastguard Worker             Assert.Throws<InvalidJsonException>(() => tokenizer.Next());
422*1b3f573fSAndroid Build Coastguard Worker         }
423*1b3f573fSAndroid Build Coastguard Worker     }
424*1b3f573fSAndroid Build Coastguard Worker }
425