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