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