Skip to content

Commit

Permalink
Add net/JSONTestSuite tests. Fix JsonPointer OOME on "[[[[..." (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
htmldoug authored Dec 21, 2021
1 parent 869c476 commit bf335cd
Show file tree
Hide file tree
Showing 323 changed files with 446 additions and 4 deletions.
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,5 @@ uPickle: a simple Scala JSON and Binary (MessagePack) serialization library
If you use uPickle/weePickle and like it, please support it by donating to lihaoyi's Patreon:

- [https://www.patreon.com/lihaoyi](https://www.patreon.com/lihaoyi)

Thanks to [JSONTestSuite](https://github.com/nst/JSONTestSuite) for the comprehensive collection of interesting JSON test files.
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package com.rallyhealth.weejson.v1.jackson

import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.{JsonParser, JsonStreamContext}
import com.fasterxml.jackson.core.base.ParserBase
import com.rallyhealth.weepickle.v1.core.TransformException

import scala.collection.compat._
import scala.collection.mutable.ArrayBuffer

object JsonParserException {

def apply(msg: String, parser: JsonParser, t: Throwable = null): Throwable = {
val parserBase = Option(parser).collect { case pb: ParserBase => pb }
new TransformException(
shortMsg = msg,
jsonPointer = splunkFriendly(parser.getParsingContext.pathAsPointer().toString),
jsonPointer = splunkFriendly(toJsonPointer(parser.getParsingContext)),
index = parserBase.map(_.getTokenCharacterOffset),
line = parserBase.map(_.getTokenLineNr),
col = parserBase.map(_.getTokenColumnNr),
Expand All @@ -19,6 +22,43 @@ object JsonParserException {
)
}

private def toJsonPointer(context: JsonStreamContext): String = {
// context.pathAsPointer().toString requires O(depth^2) memory because of
// JsonPointer._asString, and OOMEs on FromJson("[" * 100000).transform().
// For our own, we collect the nodes from leaf to root, reverse them,
// then build our string in order.
val leafToRoot =
Iterator
.iterate(Option(context))(_.flatMap(c => Option(c.getParent)))
.takeWhile(_.nonEmpty)
.flatten
.filter(_.hasPathSegment)
.to(ArrayBuffer)

leafToRoot
.reverseIterator
.foldLeft(new StringBuilder()) { (sb, ctx) =>
if (ctx.inArray()) {
sb.append('/').append(ctx.getCurrentIndex)
}
else if (ctx.inObject()) {
sb.append('/')
for {
name <- Option(ctx.getCurrentName)
c <- name
} {
c match {
case '/' => sb.append("~1")
case '~' => sb.append("~0")
case _ => sb.append(c)
}
}
}
sb
}
.result()
}

private def splunkFriendly(value: String): String = {
if (value.contains('"') || value.contains(" ")) {
"\"" + value.replace("\"", "\\\"") + "\""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.rallyhealth.weepickle.v1

import com.rallyhealth.weejson.v1.jackson.FromJson
import com.rallyhealth.weepickle.v1.core.{FromInput, NoOpVisitor, TransformException}
import org.scalactic.TypeCheckedTripleEquals
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers

import java.io.{ByteArrayInputStream, StringReader}
import java.nio.file.Files
import scala.language.{existentials, implicitConversions}

abstract class JsonPointerSpec(parse: Array[Byte] => FromInput, depthLimit: Int = 100)
extends AnyFreeSpec
with Matchers
with TypeCheckedTripleEquals {

private implicit def toBytes(s: String): Array[Byte] = s.getBytes

def assertPath(json: String, expectedPointer: String) = {
val t = intercept[TransformException](parse(json).transform(NoOpVisitor))
t.jsonPointer shouldBe expectedPointer
}

val scenarios = Seq(
("", ""),
("unquoted", ""),
(""" {"a": """, "/a"),
(""" {"a": 1, "b" """, "/b"),
(""" {"foo/bar": """, "/foo~1bar"),
(""" {"foo~bar": """, "/foo~0bar"),
(""" [0,1,2 """, "/2"),
(""" [[[ """, "/0/0"),
(""" [[0,[0,1,[ """, "/0/1/2")
)
for ((json, expected) <- scenarios) {
json in assertPath(json, expected)
}
}

class FromJsonBytesJsonPointerSpec extends JsonPointerSpec(FromJson(_))

class FromJsonStringJsonPointerSpec extends JsonPointerSpec(b => FromJson(new String(b)))

class FromJsonInputStreamJsonPointerSpec extends JsonPointerSpec(b => FromJson(new ByteArrayInputStream(b)))

class FromJsonReaderJsonPointerSpec extends JsonPointerSpec(b => FromJson(new StringReader(new String(b))))

class FromJsonPathJsonPointerSpec extends JsonPointerSpec(
b => FromJson(Files.write(Files.createTempFile("JsonPointerSpec", ".json"), b))
)

class FromJsonFileJsonPointerSpec extends JsonPointerSpec(
b => FromJson(Files.write(Files.createTempFile("JsonPointerSpec", ".json"), b).toFile)
)

Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package com.rallyhealth.weepickle.v1
import com.rallyhealth.weejson.v1.CanonicalizeNumsVisitor._
import com.rallyhealth.weejson.v1.jackson.{FromJson, ToJson, ToPrettyJson}
import com.rallyhealth.weejson.v1.{BufferedValue, GenBufferedValue}
import com.rallyhealth.weepickle.v1.core.FromInput
import com.rallyhealth.weepickle.v1.core.{FromInput, NoOpVisitor}
import org.scalactic.TypeCheckedTripleEquals
import org.scalatest.freespec.AnyFreeSpec
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks

import java.io.{ByteArrayInputStream, StringReader}
import java.io.{ByteArrayInputStream, File, StringReader}
import java.nio.file.Files
import scala.concurrent.duration._
import scala.language.{existentials, implicitConversions}
import scala.util.Try

abstract class ParserSpec(parse: Array[Byte] => FromInput, depthLimit: Int = 100)
extends AnyFreeSpec
Expand All @@ -31,6 +33,29 @@ abstract class ParserSpec(parse: Array[Byte] => FromInput, depthLimit: Int = 100
"deep arr" in testDepth(Arr(_))
"deep obj" in testDepth(b => Obj("k" -> b))

"net/JSONTestSuite" - {
for {
file <- new File("weepickle-tests/src/test/test_parsing").listFiles()
name = file.getName
if name.endsWith(".json")
} {
def parse() = FromJson(file).transform(NoOpVisitor)

name in {
val start = System.currentTimeMillis()

def duration = (System.currentTimeMillis() - start).nanos

name.head match {
case 'i' => Try(parse())
case 'y' => parse()
case 'n' => intercept[Exception](parse())
}
assert(duration < 5.seconds, s"parsing $name exceeded than the 5s time limit")
}
}
}

private def testJson(tweak: BufferedValue => BufferedValue = identity) = {
forAll { (b: BufferedValue) =>
val value = tweak(b)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[123.456e-789]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[-1e+9999]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1.5e+9999]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[-123123e100000]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[123123e100000]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[123e-10000000]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[-123123123123123123123123123123]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[100000000000000000000]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[-237462374673276894279832749832423479823246327846]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"\uDFAA":0}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["\uDADA"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["\uD888\u1234"]
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["日ш�"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["���"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["\uD800\n"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["\uDd1ea"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["\uD800\uD800\n"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["\ud800"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["\ud800abc"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[""]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["\uDd1e\uD834"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[""]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["\uDFAA"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[""]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["����"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["��"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["������"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["������"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["��"]
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1 true]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[a�]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["": 1]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[""],
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[,1]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1,,2]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["x",,]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["x"]]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["",]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["x"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[x
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[3[4]]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1:2]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[,]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[-]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[ , ""]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
["a",
4
,1,
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1,]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1,,]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[" a"\f]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[*]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1,
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[1,
1
,1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[fals]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[nul]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[tru]
Binary file not shown.
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_++.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[++1234]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_+1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[+1]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_+Inf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[+Inf]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_-01.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[-01]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_-1.0..json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[-1.0.]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_-2..json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[-2.]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_-NaN.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[-NaN]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_.-1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[.-1]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_.2e-3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[.2e-3]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_0.1.2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[0.1.2]
1 change: 1 addition & 0 deletions weepickle-tests/src/test/test_parsing/n_number_0.3e+.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[0.3e+]
Loading

0 comments on commit bf335cd

Please sign in to comment.