diff --git a/build.sbt b/build.sbt index a284f9e2d..c04dc05d8 100644 --- a/build.sbt +++ b/build.sbt @@ -107,11 +107,14 @@ lazy val xml = crossProject(JSPlatform, JVMPlatform) libraryDependencies += "junit" % "junit" % "4.12" % Test, libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test, libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.9" % Test, + libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.14.0" % Test, libraryDependencies += ("org.scala-lang" % "scala-compiler" % scalaVersion.value % Test).exclude("org.scala-lang.modules", s"scala-xml_${scalaBinaryVersion.value}") ) .jsSettings( // Scala.js cannot run forked tests - fork in Test := false + fork in Test := false, + + libraryDependencies += "org.scalacheck" %%% "scalacheck" % "1.14.0" % Test ) .jsConfigure(_.enablePlugins(ScalaJSJUnitPlugin)) diff --git a/jvm/src/test/scala/scala/xml/NodeSeqSpec.scala b/jvm/src/test/scala/scala/xml/NodeSeqSpec.scala new file mode 100644 index 000000000..069230aa4 --- /dev/null +++ b/jvm/src/test/scala/scala/xml/NodeSeqSpec.scala @@ -0,0 +1,161 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object NodeSeqSpec extends PropertiesFor("NodeSeq") + with NodeSeqGen { + + property("theSeq") = { + Prop.forAll { n: NodeSeq => + n.theSeq ne null + } + } + + property("length") = { + Prop.forAll { n: NodeSeq => + n.length >= 0 + } + } + + property("\\ \"\".throws[Exception]") = { + Prop.forAll { n: NodeSeq => + Prop.throws(classOf[IllegalArgumentException]) { + (n \ "") + } + } + } + + property("\\ _.throws[Exception]") = { + Prop.forAll { n: NodeSeq => + Prop.iff[NodeSeq](n, { + // FIXME: Exception thrown in NodeSeq.\.makeSeq + case g @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + (g \ "_") + } + case _ => { + (n \ "_") + Prop.passed + } + }) + } + } + + property("\\ @.throws[Exception]") = { + Prop.forAll { n: NodeSeq => + Prop.iff[NodeSeq](n, { + case n: NodeSeq => + Prop.throws(classOf[IllegalArgumentException]) { + (n \ "@") + } + }) + } + } + + property("\\") = { + Prop.forAll { (n: NodeSeq, s: String) => + Prop.iff[String](s, { + // FIXME: Should be IllegalArgumentException, regardless of theSeq. + case "" => + Prop.throws(classOf[IllegalArgumentException]) { + (n \ s) + } + case "@" => + Prop.throws(classOf[IllegalArgumentException]) { + (n \ s) + } + case s => + (n \ s) + Prop.passed + }) + } + } + + property("\\\\ \"\".throws[Exception]") = { + Prop.forAll { n: NodeSeq => + Prop.throws(classOf[IllegalArgumentException]) { + (n \\ "") + } + } + } + + property("\\\\ @.throws[Exception]") = { + Prop.forAll { n: NodeSeq => + Prop.iff[NodeSeq](n, { + // FIXME: Should be IllegalArgumentException, regardless of theSeq + case n if n.filter(!_.isAtom).length == 0 => + (n \\ "@") ?= NodeSeq.Empty + case n => + Prop.throws(classOf[IllegalArgumentException]) { + (n \\ "@") + } + }) + } + } + + property("\\\\") = { + Prop.forAll { (n: NodeSeq, s: String) => + Prop.iff[String](s, { + case "" | "@" => + Prop.throws(classOf[IllegalArgumentException]) { + (n \\ s) + } + case s => + (n \\ s) + Prop.passed + }) + } + } + + property("\\@ \"\".throws[Exception]") = { + Prop.forAll { n: NodeSeq => + Prop.iff[NodeSeq](n, { + case s => + Prop.throws(classOf[IllegalArgumentException]) { + (n \@ "") + } + }) + } + } + + property("\\@ _.throws[Exception]") = { + Prop.forAll { n: NodeSeq => + Prop.iff[NodeSeq](n, { + // FIXME: Exception thrown in NodeSeq.\.makeSeq + case g @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + (g \@ "_") + } + case _ => { + (n \@ "_") + Prop.passed + } + }) + } + } + + property("\\@") = { + Prop.forAll { (n: NodeSeq, s: String) => + // FIXME: Should be IllegalArgumentException, regardless of theSeq. + Prop.throws(classOf[IllegalArgumentException]) { + (n \@ s) + } || Prop.passed // FIXME: Error conditions are too complex. + } + } + + property("text") = { + Prop.forAll { n: NodeSeq => + n.text.length >= 0 + } + } +} diff --git a/jvm/src/test/scala/scala/xml/NodeSerializationSpec.scala b/jvm/src/test/scala/scala/xml/NodeSerializationSpec.scala new file mode 100644 index 000000000..5385a24db --- /dev/null +++ b/jvm/src/test/scala/scala/xml/NodeSerializationSpec.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } +import org.scalacheck.Prop.AnyOperators +import org.scalacheck.Prop.BooleanOperators + +object NodeSerializationSpec extends CheckProperties("NodeSerialization") + with NodeGen { + + property("serialization") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n => + JavaByteSerialization.roundTrip(n) ?= n + }) + } + } +} diff --git a/jvm/src/test/scala/scala/xml/XmlStringGen.scala b/jvm/src/test/scala/scala/xml/XmlStringGen.scala new file mode 100644 index 000000000..a9e26a6c6 --- /dev/null +++ b/jvm/src/test/scala/scala/xml/XmlStringGen.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait XmlStringGen extends DocumentGen { + + def xmlDeclGen(version: String, encoding: String): Gen[String] = + Gen.oneOf( + Gen.const(""), + Gen.const(s"") + ) + + val genXmlString: Gen[String] = for { + document <- Arbitrary.arbitrary[Document] + encoding <- Gen.const("UTF-8") // java.nio.charset.StandardCharsets.UTF_8.name + xmlDecl <- xmlDeclGen("1.0", encoding) + } yield { + val str = xmlDecl + Group(document.children ++ Seq(document.docElem)).toString + str + } +} diff --git a/jvm/src/test/scala/scala/xml/dtd/ExternalIDSpec.scala b/jvm/src/test/scala/scala/xml/dtd/ExternalIDSpec.scala new file mode 100644 index 000000000..c183fc82d --- /dev/null +++ b/jvm/src/test/scala/scala/xml/dtd/ExternalIDSpec.scala @@ -0,0 +1,73 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object ExternalIDSpec extends PropertiesFor("dtd.ExternalID") + with ExternalIDGen { + + property("PublicID.throws[Exception]") = { + Prop.forAll(genNonPubIdStr) { s: String => + Prop.throws(classOf[IllegalArgumentException]) { + PublicID(s, s) + } + } + } + + property("SystemID.throws[Exception]") = { + Prop.forAll(genNonSysIdStr) { s: String => + Prop.throws(classOf[IllegalArgumentException]) { + SystemID(s) + } + } + } + + property("SystemID(null).throws[Exception]") = { + Prop.throws(classOf[NullPointerException]) { + SystemID(null) + } + } + + property("label") = { + Prop.forAll { p: PublicID => + p.label ?= "#PI" + } + } + + property("attribute") = { + Prop.forAll { p: PublicID => + p.attribute ?= Node.NoAttributes + } + } + + property("child") = { + Prop.forAll { p: PublicID => + p.child ?= Nil + } + } + + property("toString") = Prop.forAll { e: ExternalID => + val str = e.toString + Prop.atLeastOne( + str ?= "", + str ?= s"""SYSTEM '${e.systemId}'""", + str ?= s"""SYSTEM "${e.systemId}"""", + str ?= s"""PUBLIC '${e.publicId}'""", + str ?= s"""PUBLIC "${e.publicId}"""", + str ?= s"""PUBLIC '${e.publicId}' '${e.systemId}'""", + str ?= s"""PUBLIC "${e.publicId}" "${e.systemId}"""", + str ?= s"""PUBLIC '${e.publicId}' "${e.systemId}"""", + str ?= s"""PUBLIC "${e.publicId}" '${e.systemId}'""" + ) + } +} diff --git a/jvm/src/test/scala/scala/xml/factory/XMLLoaderSpec.scala b/jvm/src/test/scala/scala/xml/factory/XMLLoaderSpec.scala new file mode 100644 index 000000000..15f7a16c8 --- /dev/null +++ b/jvm/src/test/scala/scala/xml/factory/XMLLoaderSpec.scala @@ -0,0 +1,211 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package factory + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +import scala.language.implicitConversions +import org.scalacheck.util.Pretty + +object XMLLoaderSpec extends PropertiesFor("factory.XMLLoader") + with DocumentGen + with NodeGen + with dtd.DocTypeGen + with XmlStringGen { + + def testParser: SAXParser = { + val parser = XML.parser + val reader = parser.getXMLReader + // Disable loading of external DTD files, to suppress + // java.io.FileNotFoundException: "dtd" (No such file or directory) + reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + parser + } + + val loader: XMLLoader[Elem] = + new XMLLoader[Elem] { + override def parser = testParser + } + + val genXMLLoader: Gen[XMLLoader[Elem]] = + Gen.const( + loader + ) + + class StreamAndWriter( + val stream: java.io.ByteArrayOutputStream, + val writer: java.io.Writer + ) + + val genStreamAndWriter: Gen[StreamAndWriter] = for { + out <- Gen.delay(new java.io.ByteArrayOutputStream) + } yield { + new StreamAndWriter( + out, + new java.io.OutputStreamWriter( + out, + "UTF-8" // java.nio.charset.StandardCharsets.UTF_8.name + ) + ) + } + + implicit val arbStreamAndWriter = Arbitrary { + genStreamAndWriter + } + + val genInputStream: Gen[java.io.InputStream] = for { + inOut <- Arbitrary.arbitrary[StreamAndWriter] + document <- Arbitrary.arbitrary[Document] + encoding <- Gen.const("UTF-8") // java.nio.charset.StandardCharsets.UTF_8.name + xmlDecl <- Gen.const(true) // Gen.oneOf(true, false) + doctype <- Arbitrary.arbitrary[dtd.DocType] + minimizeTags <- Gen.oneOf( + MinimizeMode.Default, + MinimizeMode.Always, + MinimizeMode.Never + ) + } yield { + XML.write( + inOut.writer, + Group(document.children ++ Seq(document.docElem)), + encoding, + xmlDecl, + doctype.copy(name = document.docElem.nameToString(new StringBuilder).toString), + minimizeTags + ) + inOut.writer.flush() + val str = inOut.stream.toString + new java.io.StringBufferInputStream(str) + } + + implicit val arbInputStream = Arbitrary { + genInputStream + } + + val genInputSource: Gen[InputSource] = for { + is <- Arbitrary.arbitrary[java.io.InputStream] + } yield { + Source.fromInputStream(is) + } + + implicit val arbInputSource = Arbitrary { + genInputSource + } + + property("adapter") = { + XML.adapter ne XML.adapter + } + + property("parser") = { + XML.parser ne XML.parser + } + + implicit def prettyInputStream(is: java.io.InputStream) = Pretty { p => + val builder = new StringBuilder + is.reset() + var off = 0 + val chunkSize = 65536 + val arr = new Array[Byte](chunkSize) + while (is.available() > 0) { + val len = scala.math.min(is.available(), chunkSize) + is.read(arr, off, chunkSize) + builder ++= new String(arr.take(len)) + off += len + } + builder.toString + } + + implicit def prettyInputSource(is: InputSource) = + prettyInputStream(is.getByteStream) + + // FIXME: xerces.internal.impl.io.MalformedByteSequenceException: + // Invalid byte 1 of 1-byte UTF-8 sequence. + // property("load(_: java.io.InputStream)") = { + // Prop.forAll { is: java.io.InputStream => + // loader.load(is) + // Prop.passed + // } + // } + + // FIXME: xerces.internal.impl.io.MalformedByteSequenceException: + // Invalid byte 1 of 1-byte UTF-8 sequence. + // property("load(_: InputSource)") = { + // Prop.forAll { is: InputSource => + // loader.load(is) + // Prop.passed + // } + // } + + property("loadString(_: String)") = { + // Use forAllNoShrink since Scalacheck's shrinking strategy for a + // string, removing characters byte-by-byte, won't improve the + // insight of a failure. Scalacheck is getting false feedback + // while shrinking since Xerces only throws a SAXParseException, + // albeit with different message values, but Scalacheck doesn't + // listen to them. + Prop.forAllNoShrink(genXmlString) { xml: String => + loader.loadString(xml) + Prop.passed + } + } + + property("loadString(\"\").throws[Exception]") = { + Prop.throws(classOf[org.xml.sax.SAXParseException]) { + loader.loadString("") + } + } + + property("loadString().throws[Exception]") = { + Prop.throws(classOf[org.xml.sax.SAXParseException]) { + loader.loadString("") + } + } + + property("loadString().throws[Exception]") = { + Prop.throws(classOf[org.xml.sax.SAXParseException]) { + loader.loadString("").toString ?= "" + } + } + + property("loadString(<_/>)") = { + val xml = <_/> + loader.loadString(xml.toString) ?= xml + } + + property("loadString(<_:_/>)") = { + val xml = <_:_/> + loader.loadString(xml.toString) ?= xml + } + + property("loadString(<_:_>)") = { + val xml = <_:_> + loader.loadString(xml.toString) ?= xml + } + + property("loadString(<_/>)") = { + loader.loadString("<_/>") ?= <_/> + } + + // Confirm external DTD files are not loaded, see above. + property("loadString(<_/>)") = { + loader.loadString("<_/>") ?= <_/> + } + + property("loadString(<_:_/>)") = { + val xml = """ + |<_:_/>""".stripMargin + // val is = new java.io.StringBufferInputStream(xml) + loader.loadString(xml) ?= <_:_/> + } +} diff --git a/jvm/src/test/scala/scala/xml/parsing/ConstructingParserGen.scala b/jvm/src/test/scala/scala/xml/parsing/ConstructingParserGen.scala new file mode 100644 index 000000000..cbaf9fedc --- /dev/null +++ b/jvm/src/test/scala/scala/xml/parsing/ConstructingParserGen.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package parsing + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ConstructingParserGen extends XmlStringGen { + + val genConstructingParser: Gen[ConstructingParser] = for { + xml <- Gen.oneOf( + genXmlString: Gen[String], + Arbitrary.arbitrary[String] + ) + preserveWS <- Arbitrary.arbitrary[Boolean] + } yield { + // ConstructingParser.fromSource(scala.io.Source.fromString(xml), preserveWS) + new ConstructingParser(scala.io.Source.fromString(xml), preserveWS) { + override def reportSyntaxError(str: String) = {} + } + } + + implicit val arbConstructingParser = Arbitrary { + genConstructingParser + } +} diff --git a/jvm/src/test/scala/scala/xml/parsing/ConstructingParserSpec.scala b/jvm/src/test/scala/scala/xml/parsing/ConstructingParserSpec.scala new file mode 100644 index 000000000..f0f21c6e7 --- /dev/null +++ b/jvm/src/test/scala/scala/xml/parsing/ConstructingParserSpec.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package parsing + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } + +object ConstructingParserSpec extends PropertiesFor("parsing.ConstructingParser") + with ConstructingParserGen { + + property("initialize") = { + Prop.forAll { parser: ConstructingParser => + parser.initialize eq parser + } + } + + property("prolog") = { + Prop.forAll { parser: ConstructingParser => + parser.prolog + Prop.passed + } + } + + property("document") = { + Prop.forAll { parser: ConstructingParser => + parser.document + Prop.passed + } + } +} diff --git a/jvm/src/test/scala/scala/xml/parsing/XhtmlParserGen.scala b/jvm/src/test/scala/scala/xml/parsing/XhtmlParserGen.scala new file mode 100644 index 000000000..c9256b8d4 --- /dev/null +++ b/jvm/src/test/scala/scala/xml/parsing/XhtmlParserGen.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package parsing + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait XhtmlParserGen { + + val genXhtmlParser: Gen[XhtmlParser] = for { + // FIXME: Doesn't generate valid XML strings + xml <- Arbitrary.arbitrary[String] + preserveWS <- Arbitrary.arbitrary[Boolean] + } yield { + new XhtmlParser(scala.io.Source.fromString(xml)) { + override def reportSyntaxError(str: String) = {} + } + } + + implicit val arbXhtmlParser = Arbitrary { + genXhtmlParser + } +} diff --git a/jvm/src/test/scala/scala/xml/parsing/XhtmlParserSpec.scala b/jvm/src/test/scala/scala/xml/parsing/XhtmlParserSpec.scala new file mode 100644 index 000000000..0ea7b6bcb --- /dev/null +++ b/jvm/src/test/scala/scala/xml/parsing/XhtmlParserSpec.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package parsing + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } + +object XhtmlParserSpec extends PropertiesFor("parsing.XhtmlParser") + with XhtmlParserGen { + + property("initialize") = { + Prop.forAll { parser: XhtmlParser => + parser.initialize eq parser + } + } + + property("prolog") = { + Prop.forAll { parser: XhtmlParser => + parser.prolog + Prop.passed + } + } + + property("document") = { + Prop.forAll { parser: XhtmlParser => + parser.document + Prop.passed + } + } +} diff --git a/shared/src/test/scala/scala/xml/ArbitraryElem.scala b/shared/src/test/scala/scala/xml/ArbitraryElem.scala new file mode 100644 index 000000000..a9487e57a --- /dev/null +++ b/shared/src/test/scala/scala/xml/ArbitraryElem.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ArbitraryElem { + + def genElem(sz: Int): Gen[Elem] + + implicit val arbElem: Arbitrary[Elem] +} diff --git a/shared/src/test/scala/scala/xml/ArbitraryGroup.scala b/shared/src/test/scala/scala/xml/ArbitraryGroup.scala new file mode 100644 index 000000000..9bc0cfcf2 --- /dev/null +++ b/shared/src/test/scala/scala/xml/ArbitraryGroup.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ArbitraryGroup { + + def genGroup(sz: Int): Gen[Group] + + implicit val arbGroup: Arbitrary[Group] +} diff --git a/shared/src/test/scala/scala/xml/ArbitraryMetaData.scala b/shared/src/test/scala/scala/xml/ArbitraryMetaData.scala new file mode 100644 index 000000000..abdc9cc6e --- /dev/null +++ b/shared/src/test/scala/scala/xml/ArbitraryMetaData.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ArbitraryMetaData { + + val genMetaData: Gen[MetaData] + + implicit val arbMetaData: Arbitrary[MetaData] +} diff --git a/shared/src/test/scala/scala/xml/ArbitraryNode.scala b/shared/src/test/scala/scala/xml/ArbitraryNode.scala new file mode 100644 index 000000000..dbf3663a8 --- /dev/null +++ b/shared/src/test/scala/scala/xml/ArbitraryNode.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ArbitraryNode { + + def genNode(sz: Int): Gen[Node] + + implicit val arbNode: Arbitrary[Node] +} diff --git a/shared/src/test/scala/scala/xml/ArbitraryNodeBuffer.scala b/shared/src/test/scala/scala/xml/ArbitraryNodeBuffer.scala new file mode 100644 index 000000000..5bf6ecf43 --- /dev/null +++ b/shared/src/test/scala/scala/xml/ArbitraryNodeBuffer.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ArbitraryNodeBuffer { + + val genNodeBuffer: Gen[NodeBuffer] + + implicit val arbNodeBuffer: Arbitrary[NodeBuffer] +} diff --git a/shared/src/test/scala/scala/xml/ArbitraryTextBuffer.scala b/shared/src/test/scala/scala/xml/ArbitraryTextBuffer.scala new file mode 100644 index 000000000..7097150b9 --- /dev/null +++ b/shared/src/test/scala/scala/xml/ArbitraryTextBuffer.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ArbitraryTextBuffer { + + val genTextBuffer: Gen[TextBuffer] + + implicit val arbTextBuffer: Arbitrary[TextBuffer] +} diff --git a/shared/src/test/scala/scala/xml/AtomGen.scala b/shared/src/test/scala/scala/xml/AtomGen.scala new file mode 100644 index 000000000..9a649ec68 --- /dev/null +++ b/shared/src/test/scala/scala/xml/AtomGen.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait AtomGen extends PCDataGen + with TextGen + with UnparsedGen { + + val genAtom: Gen[Atom[String]] = + Gen.oneOf(genPCData, genText, genUnparsed) + + implicit val arbAtom = Arbitrary { + genAtom + } +} diff --git a/shared/src/test/scala/scala/xml/AtomSpec.scala b/shared/src/test/scala/scala/xml/AtomSpec.scala new file mode 100644 index 000000000..ea1847b5f --- /dev/null +++ b/shared/src/test/scala/scala/xml/AtomSpec.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object AtomSpec extends PropertiesFor("Atom") + with AtomGen { + + property("new(null).throws[Exception]") = { + Prop.throws(classOf[IllegalArgumentException]) { + new Atom(null) + } + } + + property("data") = { + Prop.forAll { a: Atom[String] => + a.data ne null + } + } + + property("text") = { + Prop.forAll { a: Atom[String] => + a.text ?= s"${a.data}" + } + } +} diff --git a/shared/src/test/scala/scala/xml/AttributeGen.scala b/shared/src/test/scala/scala/xml/AttributeGen.scala new file mode 100644 index 000000000..ad29b7994 --- /dev/null +++ b/shared/src/test/scala/scala/xml/AttributeGen.scala @@ -0,0 +1,52 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait AttributeGen extends TextGen + with ArbitraryMetaData + with XmlNameGen { + + def genAttribute: Gen[Attribute] = + Gen.oneOf( + Arbitrary.arbitrary[PrefixedAttribute], + Arbitrary.arbitrary[UnprefixedAttribute] + ) + + val genPrefixedAttribute: Gen[PrefixedAttribute] = for { + prefix <- genXmlName: Gen[String] + key <- genXmlName: Gen[String] + value <- Arbitrary.arbitrary[Text] + next <- Gen.delay(genMetaData) + } yield { + new PrefixedAttribute(prefix, key, value, next) + } + + val genUnprefixedAttribute: Gen[UnprefixedAttribute] = for { + key <- genXmlName: Gen[String] + value <- Arbitrary.arbitrary[Text] + next <- Gen.delay(genMetaData) + } yield { + new UnprefixedAttribute(key, value, next) + } + + implicit val arbAttribute: Arbitrary[Attribute] = Arbitrary { + genAttribute + } + + implicit val arbPrefixedAttribute: Arbitrary[PrefixedAttribute] = Arbitrary { + genPrefixedAttribute + } + + implicit val arbUnprefixedAttribute: Arbitrary[UnprefixedAttribute] = Arbitrary { + genUnprefixedAttribute + } +} diff --git a/shared/src/test/scala/scala/xml/AttributeSpec.scala b/shared/src/test/scala/scala/xml/AttributeSpec.scala new file mode 100644 index 000000000..8009ae176 --- /dev/null +++ b/shared/src/test/scala/scala/xml/AttributeSpec.scala @@ -0,0 +1,139 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators +import org.scalacheck.Prop.BooleanOperators + +object AttributeSpec extends PropertiesFor("Attribute") + with AttributeGen + with NamespaceBindingGen + with NodeGen + with MetaDataGen { + + property("canEqual(this)") = { + Prop.forAll { a: Attribute => + a.canEqual(a) + } + } + + property("apply") = { + Prop.forAll { (a: Attribute, s: String) => + (!s.isEmpty && Utility.isNameStart(s(0))) ==> + (a(s) != s) + } + } + + property("apply(key)") = { + Prop.forAll { a: Attribute => + Prop.iff[Attribute](a, { + case p: PrefixedAttribute => + Prop.passed + case u: UnprefixedAttribute if u.key ne null => + u(u.key) ?= u.value + }) + } + } + + property("apply(uri, namespace, key)") = { + Prop.forAll { a: Attribute => + Prop.iff[Attribute](a, { + case p: PrefixedAttribute => + val res = p(p.key, NamespaceBinding(p.pre, p.key, TopScope), p.key) + res ?= p.value + case u: UnprefixedAttribute => + Prop.passed + }) + } + } + + property("isPrefixed") = { + Prop.forAll { a: Attribute => + Prop.iff[Attribute](a, { + case p: PrefixedAttribute => + a.isPrefixed ?= true + case u: UnprefixedAttribute => + a.isPrefixed ?= false + }) + } + } + + property("length") = { + Prop.forAll { a: Attribute => + a.length >= 0 + } + } + + property("size") = { + Prop.forAll { a: Attribute => + a.size >= 0 + } + } + + property("toString") = { + Prop.forAll { a: Attribute => + val str = a.toString + Prop.iff[Attribute](a, { + case a: PrefixedAttribute if a.hasNext => + str ?= s""" ${a.pre}:${a.key}="${a.value}"${a.next}""" + case a: UnprefixedAttribute if a.hasNext => + str ?= s""" ${a.key}="${a.value}"${a.next}""" + case a: PrefixedAttribute => + str ?= s""" ${a.pre}:${a.key}="${a.value}"""" + case a: UnprefixedAttribute => + str ?= s""" ${a.key}="${a.value}"""" + }) + } + } + + property("unapply") = { + Prop.forAll { a: Attribute => + Attribute.unapply(a) ?= Some((a.key, a.value, a.next)) + } + } + + property("remove(key)") = { + Prop.forAll { a: Attribute => + Prop.iff[Attribute](a, { + case a: PrefixedAttribute => + a.remove(a.key) ?= a.copy(a.next.remove(a.key)) // FIXME: ??? + case a: UnprefixedAttribute => + a.remove(a.key) ?= a.next + }) + } + } + + property("remove") = { + Prop.forAll { (a: Attribute, s: String) => + s != a.key ==> + (a.remove(s) ?= a.copy(a.next.remove(s))) + } + } + + property("getNamespace") = { + Prop.forAll { (a: Attribute, n: Node) => + a.getNamespace(n) ?= n.getNamespace(a.pre) + } + } + + property("wellformed") = { + Prop.forAll { (a: Attribute, scope: NamespaceBinding) => + Prop.iff[Attribute](a, { + case a: PrefixedAttribute => + val res = a.wellformed(scope) + res ?= ((null eq a.next(scope.getURI(a.pre), scope, a.key)) && a.next.wellformed(scope)) + case a: UnprefixedAttribute => + val res = a.wellformed(scope) + res ?= ((null eq a.next(null, scope, a.key)) && a.next.wellformed(scope)) + }) + } + } +} diff --git a/shared/src/test/scala/scala/xml/CommentGen.scala b/shared/src/test/scala/scala/xml/CommentGen.scala new file mode 100644 index 000000000..9dfd3e62b --- /dev/null +++ b/shared/src/test/scala/scala/xml/CommentGen.scala @@ -0,0 +1,25 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait CommentGen extends Utf8StringGen { + + val genComment: Gen[Comment] = for { + s <- genUtf8String: Gen[String] if !s.contains("--") && !s.endsWith("-") + } yield { + Comment(s) + } + + implicit val arbComment = Arbitrary { + genComment + } +} diff --git a/shared/src/test/scala/scala/xml/CommentSpec.scala b/shared/src/test/scala/scala/xml/CommentSpec.scala new file mode 100644 index 000000000..50bfed0a9 --- /dev/null +++ b/shared/src/test/scala/scala/xml/CommentSpec.scala @@ -0,0 +1,45 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object CommentSpec extends PropertiesFor("Comment") + with CommentGen { + + property("new(--).throws[Exception]") = { + Prop.throws(classOf[IllegalArgumentException]) { + new Comment("--") + } + } + + property("child") = { + Prop.forAll { n: Comment => + n.child ?= Nil + } + } + + property("text") = { + Prop.forAll { n: Comment => + n.text ?= "" + } + } + + property("toString") = { + Prop.forAll { n: Comment => + val str = n.toString + Prop.atLeastOne( + str ?= "", + str.startsWith("") + ) + } + } +} diff --git a/shared/src/test/scala/scala/xml/DocumentGen.scala b/shared/src/test/scala/scala/xml/DocumentGen.scala new file mode 100644 index 000000000..db24f8618 --- /dev/null +++ b/shared/src/test/scala/scala/xml/DocumentGen.scala @@ -0,0 +1,71 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait DocumentGen extends NodeGen + with dtd.DocTypeGen + with dtd.DTDGen { + + val genDocument: Gen[Document] = for { + prolog <- genProlog + dtd_ <- Arbitrary.arbitrary[dtd.DTD] + elements <- Arbitrary.arbitrary[Elem] + n <- Gen.choose(0, 4) + misc <- Gen.listOfN(n, genMisc) + } yield { + new Document { + children = prolog + dtd = dtd_ + docElem = elements + } + } + + implicit val arbDocument = Arbitrary { + genDocument + } + + def genWhiteSpace: Gen[String] = + Gen.listOf(Gen.oneOf(' ', '\t', '\n', '\r')).map(_.mkString) + + def genMisc: Gen[Node] = + Gen.oneOf( + Arbitrary.arbitrary[Comment], + Arbitrary.arbitrary[ProcInstr], + genWhiteSpace.map(Text(_)) + ) + + // Seems the intention was to have the Document.children contain the + // XML "prologue", but it would need to contain XML declarations and + // DocType declarations, which are are not of type Node, and + // therefore wouldn't exist in a Seq[Node]. Oh, well. XML.write + // ended up avoiding the Document type, and just taking the values + // it needed as arguments rather than from class fields. + def genProlog: Gen[NodeSeq] = for { + // xmlDecl <- genXmlDecl + n <- Gen.choose(0, 4) + misc <- Gen.listOfN(n, genMisc) + // docType <- Arbitrary.arbitrary[dtd.DocType] + // misc2 <- Gen.listOf(genMisc) + // prolog2 <- Gen.const(Text(docType.toString) :: misc2) + // prolog <- Gen.oneOf( + // xmlDecl + misc + prolog2, + // Gen.const(misc ++ prolog2), + // Gen.const(misc) + // ) + } yield { + // NodeSeq.fromSeq(prolog) + NodeSeq.fromSeq(misc) + } + + val genXmlDecl: Gen[String] = + Gen.const("") +} diff --git a/shared/src/test/scala/scala/xml/DocumentSpec.scala b/shared/src/test/scala/scala/xml/DocumentSpec.scala new file mode 100644 index 000000000..7c7c963d9 --- /dev/null +++ b/shared/src/test/scala/scala/xml/DocumentSpec.scala @@ -0,0 +1,86 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object DocumentSpec extends PropertiesFor("Document") + with DocumentGen { + + property("baseURI") = { + Prop.forAll { d: Document => + d.baseURI eq null + } + } + + property("children") = { + Prop.forAll { d: Document => + Prop.atLeastOne( + d.children eq null, + d.isInstanceOf[Seq[Node]] + ) + } + } + + property("docElem") = { + Prop.forAll { d: Document => + Prop.atLeastOne( + d.theSeq eq null, + d.theSeq.isInstanceOf[Node] + ) + } + } + + property("dtd") = { + Prop.forAll { d: Document => + Prop.atLeastOne( + d.dtd eq null, + d.dtd.isInstanceOf[dtd.DTD] + ) + } + } + + property("encoding") = { + Prop.forAll { d: Document => + d.encoding eq null + } + } + + property("standAlone") = { + Prop.forAll { d: Document => + d.standAlone eq null + } + } + + property("version") = { + Prop.forAll { d: Document => + d.version eq null + } + } + + property("allDeclarationsProcessed") = { + Prop.forAll { d: Document => + d.allDeclarationsProcessed ?= false + } + } + + property("theSeq") = { + Prop.forAll { d: Document => + d.theSeq eq d.docElem + } + } + + property("canEqual") = { + Prop.forAll { d: Document => + d.canEqual(d) + } + } +} diff --git a/shared/src/test/scala/scala/xml/ElemGen.scala b/shared/src/test/scala/scala/xml/ElemGen.scala new file mode 100644 index 000000000..33a4b90a0 --- /dev/null +++ b/shared/src/test/scala/scala/xml/ElemGen.scala @@ -0,0 +1,66 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ElemGen extends NamespaceBindingGen + with ArbitraryNode + with MetaDataGen + with XmlNameGen { + + val genStringOrNull: Gen[String] = + Gen.oneOf( + genXmlName: Gen[String], + Gen.const(null) + ) + + def genElem(sz: Int): Gen[Elem] = for { + prefix <- genStringOrNull + label <- genXmlName: Gen[String] + attribs <- Arbitrary.arbitrary[MetaData] + scope <- Arbitrary.arbitrary[NamespaceBinding] + minimizeEmpty <- Arbitrary.arbitrary[Boolean] + n <- Gen.choose(0, scala.math.sqrt(sz / 2).toInt) + children <- Gen.listOfN(n, genNode(n)) + } yield { + Elem( + prefix, + label, + attribs, + scope, + minimizeEmpty, + children: _* + ) + } + + def invalidElem(sz: Int): Gen[Elem] = for { + prefix <- Gen.const("") + label <- Arbitrary.arbitrary[String] + attribs <- Arbitrary.arbitrary[MetaData] + scope <- Arbitrary.arbitrary[NamespaceBinding] + minimizeEmpty <- Arbitrary.arbitrary[Boolean] + n <- Gen.choose(0, scala.math.sqrt(sz / 2).toInt) + children <- Gen.listOfN(n, genNode(n)) + } yield { + Elem( + prefix, + label, + attribs, + scope, + minimizeEmpty, + children: _* + ) + } + + implicit val arbElem = Arbitrary { + Gen.sized(sz => genElem(sz)) + } +} diff --git a/shared/src/test/scala/scala/xml/ElemSpec.scala b/shared/src/test/scala/scala/xml/ElemSpec.scala new file mode 100644 index 000000000..864188fda --- /dev/null +++ b/shared/src/test/scala/scala/xml/ElemSpec.scala @@ -0,0 +1,70 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators +import org.scalacheck.Prop.BooleanOperators + +object ElemSpec extends PropertiesFor("Elem") + with NodeSeqGen + with MetaDataGen + with NamespaceBindingGen { + + property("new") = { + Prop.forAll { (prefix: String, label: String, attribs: MetaData, + scope: NamespaceBinding, minimizeEmpty: Boolean, + children: NodeSeq) => + Prop.atLeastOne( + !prefix.isEmpty ==> { + new Elem( + prefix, + label, + attribs, + scope, + minimizeEmpty, + children.theSeq: _* + ) + Prop.passed + }, + prefix.isEmpty ==> + Prop.throws(classOf[IllegalArgumentException]) { + new Elem( + prefix, + label, + attribs, + scope, + minimizeEmpty, + children.theSeq: _* + ) + } + ) + } + } + + property("unapplySeq(Elem)") = { + Prop.forAll { e: Elem => + val opt = Elem.unapplySeq(e) + opt ?= Some((e.prefix, e.label, e.attributes, e.scope, e.child)) + } + } + + property("unapplySeq(Node)") = { + Prop.forAll { n: Node => + val opt = Elem.unapplySeq(n) + Prop.iff[Node](n, { + case _: SpecialNode | _: Group => + opt ?= None + case _ => + opt ?= Some((n.prefix, n.label, n.attributes, n.scope, n.child.toSeq)) + }) + } + } +} diff --git a/shared/src/test/scala/scala/xml/EntityRefGen.scala b/shared/src/test/scala/scala/xml/EntityRefGen.scala new file mode 100644 index 000000000..9e5be4944 --- /dev/null +++ b/shared/src/test/scala/scala/xml/EntityRefGen.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait EntityRefGen extends XmlNameGen { + + val genEntityRef: Gen[EntityRef] = for { + // FIXME: Throws SAXParseException: The entity "{0}" was + // referenced, but not declared. + // s <- genXmlName // Would need a corresponding + // // declaration for `s' to be well-formed. + // Instead, only generate the pre-defined ones. + s <- Gen.oneOf("quot", "amp", "apos", "lt", "gt") + } yield { + new EntityRef(s) + } + + implicit val arbEntityRef = Arbitrary { + genEntityRef + } +} diff --git a/shared/src/test/scala/scala/xml/GroupGen.scala b/shared/src/test/scala/scala/xml/GroupGen.scala new file mode 100644 index 000000000..2ec85db36 --- /dev/null +++ b/shared/src/test/scala/scala/xml/GroupGen.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait GroupGen extends ArbitraryGroup + with ArbitraryNode { + + def genGroup(sz: Int): Gen[Group] = for { + n <- Gen.choose(0, scala.math.sqrt(sz / 2).toInt) + nodes <- Gen.listOfN(n, genNode(n)) + } yield { + Group(nodes) + } + + implicit val arbGroup = Arbitrary { + Gen.sized(sz => genGroup(sz)) + } +} diff --git a/shared/src/test/scala/scala/xml/GroupSpec.scala b/shared/src/test/scala/scala/xml/GroupSpec.scala new file mode 100644 index 000000000..e603fd0c8 --- /dev/null +++ b/shared/src/test/scala/scala/xml/GroupSpec.scala @@ -0,0 +1,80 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object GroupSpec extends PropertiesFor("Group") + with NodeGen + with ElemGen + with GroupGen { + + property("attributes") = + Prop.forAll { g: Group => + Prop.throws(classOf[UnsupportedOperationException]) { + g.attributes + } + } + + property("child") = + Prop.forAll { g: Group => + Prop.throws(classOf[UnsupportedOperationException]) { + g.child + } + } + + property("label") = + Prop.forAll { g: Group => + Prop.throws(classOf[UnsupportedOperationException]) { + g.label + } + } + + property("namespace") = + Prop.forAll { g: Group => + Prop.throws(classOf[UnsupportedOperationException]) { + g.namespace + } + } + + property("descendant") = + Prop.forAll { g: Group => + Prop.throws(classOf[UnsupportedOperationException]) { + g.descendant + } + } + + property("descendant_or_self") = + Prop.forAll { g: Group => + Prop.throws(classOf[UnsupportedOperationException]) { + g.descendant_or_self + } + } + + property("empty.\\.throws[Exception]") = { + val g = Group(List()) + (g \ "a") ?= NodeSeq.Empty + } + + // FIXME: UnsupportedOperationException: class Group does not support method 'child' + // property("\\.throws[Exception]") = { + // Prop.forAll { g: Group => + // (g \ "a") + // Prop.passed // FIXME: Check that the result is actually correct. + // } + // } + + property("theSeq") = { + Prop.forAll { g: Group => + g.theSeq ?= g.nodes + } + } +} diff --git a/shared/src/test/scala/scala/xml/MetaDataGen.scala b/shared/src/test/scala/scala/xml/MetaDataGen.scala new file mode 100644 index 000000000..603e57ce0 --- /dev/null +++ b/shared/src/test/scala/scala/xml/MetaDataGen.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait MetaDataGen extends AttributeGen + with ArbitraryMetaData { + + val genMetaData: Gen[MetaData] = for { + metaData <- Gen.oneOf(Gen.const(Null), genAttribute) + } yield { + metaData + } + + implicit val arbMetaData = Arbitrary { + genMetaData + } +} diff --git a/shared/src/test/scala/scala/xml/MetaDataSpec.scala b/shared/src/test/scala/scala/xml/MetaDataSpec.scala new file mode 100644 index 000000000..42d37242b --- /dev/null +++ b/shared/src/test/scala/scala/xml/MetaDataSpec.scala @@ -0,0 +1,106 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators +import org.scalacheck.Prop.BooleanOperators + +object MetaDataSpec extends PropertiesFor("MetaData") + with ElemGen + with GroupGen + with NodeGen + with MetaDataGen { + + property("canEqual(this)") = { + Prop.forAll { m: MetaData => + m.canEqual(m) + } + } + + property("apply") = { + Prop.forAll { (m: MetaData, s: String) => + ((!s.isEmpty && Utility.isNameStart(s(0))) ==> (m(s) != s)) + } + } + + property("apply(key)") = { + Prop.forAll { m: MetaData => + Prop.iff[MetaData](m, { + case p: PrefixedAttribute => Prop.passed + case u: UnprefixedAttribute if u.key ne null => u(u.key) ?= u.value + case Null => Prop.passed + }) + } + } + + property("apply(uri, namespace, key)") = { + Prop.forAll { m: MetaData => + Prop.iff[MetaData](m, { + case p: PrefixedAttribute => + p(p.key, NamespaceBinding(p.pre, p.key, TopScope), p.key) ?= p.value + case u: UnprefixedAttribute => Prop.passed + case Null => Prop.passed + }) + } + } + + property("isPrefixed") = { + Prop.forAll { m: MetaData => + Prop.atLeastOne( + m.isPrefixed ?= true, + m.isPrefixed ?= false + ) + } + } + + property("iterator") = { + Prop.forAll { m: MetaData => + Prop.iff[MetaData](m, { + case a: Attribute if a.value eq null => + a.iterator.sameElements(a.next.iterator) + case a: Attribute => + a.iterator.sameElements(Iterator.single(a) ++ a.next.iterator) + case Null => + m.iterator ?= Iterator.empty + }) + } + } + + property("length") = { + Prop.forAll { m: MetaData => + m.length >= 0 + } + } + + property("size") = { + Prop.forAll { m: MetaData => + m.size >= 0 + } + } + + property("toString") = { + Prop.forAll { m: MetaData => + val str = m.toString + Prop.iff[MetaData](m, { + case m: MetaData if m.value eq null => + str ?= "" + case a: PrefixedAttribute if a.hasNext => + str ?= s""" ${a.pre}:${a.key}="${a.value}"${a.next}""" + case a: UnprefixedAttribute if a.hasNext => + str ?= s""" ${a.key}="${a.value}"${a.next}""" + case a: PrefixedAttribute => + str ?= s""" ${a.pre}:${a.key}="${a.value}"""" + case a: UnprefixedAttribute => + str ?= s""" ${a.key}="${a.value}"""" + }) + } + } +} diff --git a/shared/src/test/scala/scala/xml/NamespaceBindingGen.scala b/shared/src/test/scala/scala/xml/NamespaceBindingGen.scala new file mode 100644 index 000000000..0fae8eeb4 --- /dev/null +++ b/shared/src/test/scala/scala/xml/NamespaceBindingGen.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait NamespaceBindingGen { + + val genDepth: Gen[NamespaceBinding] = for { + prefix <- Arbitrary.arbitrary[String] if !prefix.isEmpty + uri <- Arbitrary.arbitrary[String] + parent <- genNamespaceBinding + } yield { + NamespaceBinding(prefix, uri, parent) + } + + val genNamespaceBinding: Gen[NamespaceBinding] = for { + // FIXME: Throws SAXParseException: prefix "{0}" was not declared. + // Need to writer generator that can write namespace declaration + // namespace <- Gen.oneOf(Gen.const(TopScope), genDepth) + namespace <- Gen.const(TopScope) + } yield { + namespace + } + + implicit val arbNamespaceBinding = Arbitrary { + genNamespaceBinding + } +} diff --git a/shared/src/test/scala/scala/xml/NamespaceBindingSpec.scala b/shared/src/test/scala/scala/xml/NamespaceBindingSpec.scala new file mode 100644 index 000000000..7f436cbe9 --- /dev/null +++ b/shared/src/test/scala/scala/xml/NamespaceBindingSpec.scala @@ -0,0 +1,57 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object NamespaceBindingSpec extends PropertiesFor("NamespaceBinding") + with NamespaceBindingGen { + + property("canEqual(this)") = { + Prop.forAll { n: NamespaceBinding => + n.canEqual(n) + } + } + + property("getPrefix") = { + Prop.forAll { (n: NamespaceBinding, s: String) => + n.getPrefix(s) != s + } + } + + property("getPrefix(uri)") = { + Prop.forAll { n: NamespaceBinding => + n.getPrefix(n.uri) == n.prefix + } + } + + property("getURI") = { + Prop.forAll { (n: NamespaceBinding, s: String) => + n.getURI(s) != s + } + } + + property("getURI(prefix)") = { + Prop.forAll { n: NamespaceBinding => + n.getURI(n.prefix) == n.uri + } + } + + property("toString") = { + Prop.forAll { n: NamespaceBinding => + val str = n.toString + Prop.atLeastOne( + str ?= "", + n.toString.startsWith(" xmlns:") + ) + } + } +} diff --git a/shared/src/test/scala/scala/xml/NodeBufferGen.scala b/shared/src/test/scala/scala/xml/NodeBufferGen.scala new file mode 100644 index 000000000..dd05af938 --- /dev/null +++ b/shared/src/test/scala/scala/xml/NodeBufferGen.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait NodeBufferGen extends ArbitraryNodeBuffer + with NodeGen { + + implicit val arbNodeBuffer = Arbitrary { + genNodeBuffer + } + + val genNodeBuffer: Gen[NodeBuffer] = + Gen.delay(new NodeBuffer) +} diff --git a/shared/src/test/scala/scala/xml/NodeBufferSpec.scala b/shared/src/test/scala/scala/xml/NodeBufferSpec.scala new file mode 100644 index 000000000..e97b21ccf --- /dev/null +++ b/shared/src/test/scala/scala/xml/NodeBufferSpec.scala @@ -0,0 +1,50 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object NodeBufferSpec extends PropertiesFor("NodeBuffer") + with NodeBufferGen + with NodeGen { + + implicit val arbAny = Arbitrary { + genAny + } + + val genAny: Gen[Any] = + Gen.oneOf( + Gen.const(null), + Gen.const((): Unit), + Arbitrary.arbitrary[Node], + Arbitrary.arbitrary[List[Node]].map(_.toIterator), + Arbitrary.arbitrary[List[Node]].map(_.toIterable), + Arbitrary.arbitrary[Array[Node]] + ) + + property("&+") = { + Prop.forAll { (nb: NodeBuffer, a: Any) => + nb&+(a) + Prop.passed + } + } + + property("toString") = { + Prop.forAll { n: NodeBuffer => + Prop.all( + n.toString ?= "NodeBuffer()", + (n &+ "").toString.startsWith("NodeBuffer") + ) + } + } +} diff --git a/shared/src/test/scala/scala/xml/NodeGen.scala b/shared/src/test/scala/scala/xml/NodeGen.scala new file mode 100644 index 000000000..628caf17c --- /dev/null +++ b/shared/src/test/scala/scala/xml/NodeGen.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait NodeGen extends ArbitraryNode + with ElemGen + // FIXME: UnsupportedOperationException: class Group does not support method + // with GroupGen + with AtomGen + with CommentGen + with EntityRefGen + with ProcInstrGen { + + def genNode(sz: Int): Gen[Node] = + Gen.oneOf( + genAtom, + genComment, + Gen.delay(genElem(scala.math.sqrt(sz / 2).toInt)), + genEntityRef, + // Gen.delay(genGroup(scala.math.sqrt(sz / 2).toInt)), // FIXME: See above. + genProcInstr + ) + + implicit val arbNode = Arbitrary { + Gen.sized(sz => genNode(sz)) + } +} diff --git a/shared/src/test/scala/scala/xml/NodeSeqGen.scala b/shared/src/test/scala/scala/xml/NodeSeqGen.scala new file mode 100644 index 000000000..d18dd38e2 --- /dev/null +++ b/shared/src/test/scala/scala/xml/NodeSeqGen.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait NodeSeqGen extends NodeGen { + + implicit val arbNodeSeq = Arbitrary { + Gen.sized(sz => genNodeSeq(sz)) + } + + def genNodeSeq(sz: Int): Gen[NodeSeq] = for { + n <- Gen.choose(0, scala.math.sqrt(sz / 2).toInt) + empty <- Gen.const(NodeSeq.Empty) + s <- Gen.listOfN(n, genNode(n)) + nodeSeq <- Gen.oneOf(empty, NodeSeq.fromSeq(s.toSeq)) + } yield { + nodeSeq + } +} diff --git a/shared/src/test/scala/scala/xml/NodeSpec.scala b/shared/src/test/scala/scala/xml/NodeSpec.scala new file mode 100644 index 000000000..de8274445 --- /dev/null +++ b/shared/src/test/scala/scala/xml/NodeSpec.scala @@ -0,0 +1,347 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } +import org.scalacheck.Prop.AnyOperators +import org.scalacheck.Prop.BooleanOperators + +object NodeSpec extends CheckProperties("Node") + with NodeGen { + + property("canEqual(this)") = { + Prop.forAll { n: Node => + n.canEqual(n) + } + } + + property("canEqual") = { + Prop.forAll { (n1: Node, n2: Node) => + val b = n1.canEqual(n2) + Prop.atLeastOne( + b ?= true, + b ?= false + ) + } + } + + property("prefix") = { + Prop.forAll { n: Text => + Prop.iff[Node](n, { + case n @ Comment(_) => n.prefix eq null + case n @ EntityRef(_) => n.prefix eq null + case n: PCData => n.prefix eq null + case n @ ProcInstr(_, _) => n.prefix eq null + case n: Text => n.prefix eq null + case n: Unparsed => n.prefix eq null + case e: Elem => + Prop.atLeastOne( + // e.prefix eq null, // FIXME: NullPointerException: null + e.prefix eq null, + e.prefix != "" + ) + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.label + } + case a: Atom[_] => n.prefix eq null + }) + } + } + + property("label") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Comment(_) => n.label ?= "#REM" + case n @ EntityRef(_) => n.label ?= "#ENTITY" + case n: PCData => n.label ?= "#PCDATA" + case n @ ProcInstr(_, _) => n.label ?= "#PI" + case n: Text => n.label ?= "#PCDATA" + case n: Unparsed => n.label ?= "#PCDATA" + case n: Elem => n.label.length >= 0 + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.label + } + case n: Atom[_] => n.label ?= "#PCDATA" + }) + } + } + + property("isAtom") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n: Atom[_] => n.isAtom ?= true + case _ => n.isAtom ?= false + }) + } + } + + property("doCollectNamespaces") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Comment(_) => n.doCollectNamespaces ?= false + case n @ EntityRef(_) => n.doCollectNamespaces ?= false + case n @ ProcInstr(_, _) => n.doCollectNamespaces ?= false + case n: PCData => n.doCollectNamespaces ?= false + case n: Unparsed => n.doCollectNamespaces ?= false + case n: Text => n.doCollectNamespaces ?= false + case n: Atom[_] => n.doCollectNamespaces ?= false + case n: Elem => n.doCollectNamespaces ?= true + case n @ Group(_) => n.doCollectNamespaces ?= true + }) + } + } + + property("doTransform") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Comment(_) => n.doTransform ?= false + case n @ EntityRef(_) => n.doTransform ?= false + case n @ ProcInstr(_, _) => n.doTransform ?= false + case n: PCData => n.doTransform ?= false + case n: Unparsed => n.doTransform ?= false + case n: Text => n.doTransform ?= false + case n: Atom[_] => n.doTransform ?= false + case n: Elem => n.doTransform ?= true + case n @ Group(_) => n.doTransform ?= true + }) + } + } + + property("scope") = { + Prop.forAll { n: Node => + n.scope ne null + } + } + + property("namespace") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.namespace + } + case _ => + n.namespace ?= n.getNamespace(n.prefix) + }) + } + } + + // FIXME: NoSuchElementException thrown by Null.apply + // property("attribute(\"\")") = { + // Prop.forAll { n: Node => + // Prop.throws(classOf[IllegalArgumentException]) { + // n.attribute("") + // } + // } + // } + + property("attribute") = { + Prop.forAll { (n: Node, s: String) => + Prop.iff[Node](n, { + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.attribute(s) + } + case _ => + Prop.atLeastOne( + s.isEmpty ==> + Prop.throws(classOf[NoSuchElementException]) { + n.attribute(s) + } , + ((!s.isEmpty) && Utility.isNameStart(s.head)) ==> + Prop.throws(classOf[NoSuchElementException]) { + n.attribute(s) + }, + ((!s.isEmpty) && !Utility.isNameStart(s.head)) ==> + Prop.throws(classOf[IllegalArgumentException]) { + n.attribute(s) + } + ) + }) + } + } + + property("attributes") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Comment(_) => n.attributes ?= Null + case n @ EntityRef(_) => n.attributes ?= Null + case n: PCData => n.attributes ?= Null + case n @ ProcInstr(_, _) => n.attributes ?= Null + case n: Text => n.attributes ?= Null + case n: Unparsed => n.attributes ?= Null + case n: Elem => n.attributes.length >= 0 + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.attributes + } + }) + } + } + + property("child") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Comment(_) => n.child ?= Nil + case n @ EntityRef(_) => n.child ?= Nil + case n: PCData => n.child ?= Nil + case n @ ProcInstr(_, _) => n.child ?= Nil + case n: Text => n.child ?= Nil + case n: Unparsed => n.child ?= Nil + case n: Elem => n.child.length >= 0 + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.child + } + }) + } + } + + property("nonEmptyChildren") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Comment(_) => n.nonEmptyChildren ?= Nil + case n @ EntityRef(_) => n.nonEmptyChildren ?= Nil + case n: PCData => n.nonEmptyChildren ?= Nil + case n @ ProcInstr(_, _) => n.nonEmptyChildren ?= Nil + case n: Text => n.nonEmptyChildren ?= Nil + case n: Unparsed => n.nonEmptyChildren ?= Nil + case n: Elem => n.nonEmptyChildren.length >= 0 + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.nonEmptyChildren + } + }) + } + } + + property("descendant") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Comment(_) => n.descendant ?= Nil + case n @ EntityRef(_) => n.descendant ?= Nil + case n: PCData => n.descendant ?= Nil + case n @ ProcInstr(_, _) => n.descendant ?= Nil + case n: Text => n.descendant ?= Nil + case n: Unparsed => n.descendant ?= Nil + case n: Elem => n.descendant.length >= 0 + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.descendant + } + }) + } + } + + property("descendant_or_self") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Comment(_) => n.descendant_or_self ?= List(n) + case n @ EntityRef(_) => n.descendant_or_self ?= List(n) + case n: PCData => n.descendant_or_self ?= List(n) + case n @ ProcInstr(_, _) => n.descendant_or_self ?= List(n) + case n: Text => n.descendant_or_self ?= List(n) + case n: Unparsed => n.descendant_or_self ?= List(n) + case n: Elem => n.descendant_or_self ?= n :: n.descendant + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.descendant_or_self + } + }) + } + } + + property("theSeq") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Group(nodes) => + n.theSeq ?= nodes + case _ => + n.theSeq ?= n :: Nil + }) + } + } + + property("xmlType") = { + Prop.forAll { n: Node => + n.xmlType eq null + } + } + + property("nameToString") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + n.namespace + } + case _ => { + val str = n.nameToString(new StringBuilder).toString + Prop.atLeastOne( + str ?= s"${n.prefix}:${n.label}", + str ?= s"${n.label}" + ) + } + }) + } + } + + property("text") = { + Prop.forAll { n: Node => + n.text.length >= 0 + } + } + + property("toString") = { + Prop.forAll { n: Node => + n.toString.length >= 0 + } + } + + property("match") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Comment(commentText: String) => + commentText ?= n.commentText + case Elem(null, _: String, _: MetaData, _: NamespaceBinding, _ @ _*) => + Prop.passed + case Elem(_: String, _: String, _: MetaData, _: NamespaceBinding, _ @ _*) => + Prop.passed + case n @ EntityRef(entityName) => + entityName ?= n.entityName + case n @ Group(nodes) => + Prop.passed + case n @ PCData(data) => + PCData.unapply(n) ?= Some(data) + case n @ ProcInstr(_: String, _: String) => + Prop.passed + case n @ Text(_: String) => + Prop.passed + case n @ Unparsed(_: String) => + Prop.passed + }) + } + } + + property("unapplySeq") = { + Prop.forAll { n: Node => + Prop.iff[Node](n, { + case n @ Group(_) => + Prop.throws(classOf[UnsupportedOperationException]) { + Node.unapplySeq(n) + } + case _ => + Node.unapplySeq(n) ?= Some((n.label, n.attributes, n.child.toSeq)) + }) + } + } +} diff --git a/shared/src/test/scala/scala/xml/PCDataGen.scala b/shared/src/test/scala/scala/xml/PCDataGen.scala new file mode 100644 index 000000000..5b141aee7 --- /dev/null +++ b/shared/src/test/scala/scala/xml/PCDataGen.scala @@ -0,0 +1,25 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait PCDataGen extends Utf8StringGen { + + val genPCData: Gen[PCData] = for { + s <- genUtf8String: Gen[String] + } yield { + PCData(s) + } + + implicit val arbPCData = Arbitrary { + genPCData + } +} diff --git a/shared/src/test/scala/scala/xml/PCDataSpec.scala b/shared/src/test/scala/scala/xml/PCDataSpec.scala new file mode 100644 index 000000000..86e65a026 --- /dev/null +++ b/shared/src/test/scala/scala/xml/PCDataSpec.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } +import org.scalacheck.Prop.AnyOperators + +object PCDataSpec extends CheckProperties("PCData") + with PCDataGen { + + property("text") = { + Prop.forAll { d: PCData => + d.text ?= d.data + } + } + + property("toString") = { + Prop.forAll { d: PCData => + d.toString ?= s"" + } + } + + property("unapply") = { + Prop.forAll { d: PCData => + PCData.unapply(d) ?= Some(d.data) + } + } +} diff --git a/shared/src/test/scala/scala/xml/PrettyPrinterGen.scala b/shared/src/test/scala/scala/xml/PrettyPrinterGen.scala new file mode 100644 index 000000000..441db67bd --- /dev/null +++ b/shared/src/test/scala/scala/xml/PrettyPrinterGen.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait PrettyPrinterGen { + + implicit val arbPrettyPrinter = Arbitrary { + genPrettyPrinter + } + + val genPrettyPrinter: Gen[PrettyPrinter] = for { + width <- Gen.posNum[Int] + step <- Gen.posNum[Int] + } yield { + new PrettyPrinter(width, step) + } +} diff --git a/shared/src/test/scala/scala/xml/PrettyPrinterSpec.scala b/shared/src/test/scala/scala/xml/PrettyPrinterSpec.scala new file mode 100644 index 000000000..67e88a236 --- /dev/null +++ b/shared/src/test/scala/scala/xml/PrettyPrinterSpec.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } + +object PrettyPrinterSpec extends CheckProperties("PrettyPrinter") + with PrettyPrinterGen + with NodeGen { + + property("format") = { + Prop.forAll { (pp: PrettyPrinter, n: Node) => + pp.format(n).length >= 0 + } + } + + property("formatNodes") = { + Prop.forAll { (pp: PrettyPrinter, ns: Seq[Node]) => + pp.formatNodes(ns).length >= 0 + } + } +} diff --git a/shared/src/test/scala/scala/xml/ProcInstrGen.scala b/shared/src/test/scala/scala/xml/ProcInstrGen.scala new file mode 100644 index 000000000..f73b9f659 --- /dev/null +++ b/shared/src/test/scala/scala/xml/ProcInstrGen.scala @@ -0,0 +1,46 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ProcInstrGen extends XmlNameGen { + + val validTarget: Gen[String] = + genXmlName.suchThat(_.toLowerCase != "xml") + + val validProcText: Gen[String] = + genXmlName.suchThat(!_.contains("?>")): Gen[String] + + val genProcInstr: Gen[ProcInstr] = for { + target <- validTarget + proctext <- validProcText + } yield { + new ProcInstr(target, proctext) + } + + def invalidProcInstr: Gen[ProcInstr] = for { + badTarget <- Gen.oneOf(invalidXmlName, Gen.oneOf("XML", "xml", "Xml")) + badProcText <- Gen.const("?>") + goodTarget <- validTarget + goodProcText <- validProcText + procinstr <- Gen.oneOf( + Gen.delay(new ProcInstr(goodTarget, badProcText)), + Gen.delay(new ProcInstr(badTarget, goodProcText)), + Gen.delay(new ProcInstr(badTarget, badProcText)) + ) + } yield { + procinstr + } + + implicit val arbProcInstr = Arbitrary { + genProcInstr + } +} diff --git a/shared/src/test/scala/scala/xml/ProcInstrSpec.scala b/shared/src/test/scala/scala/xml/ProcInstrSpec.scala new file mode 100644 index 000000000..cffb83894 --- /dev/null +++ b/shared/src/test/scala/scala/xml/ProcInstrSpec.scala @@ -0,0 +1,57 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } +import org.scalacheck.Prop.AnyOperators + +object ProcInstrSpec extends CheckProperties("ProcInstr") + with ProcInstrGen { + + property("label") = { + Prop.forAll { p: ProcInstr => + p.label == "#PI" + } + } + + property("text") = { + Prop.forAll { p: ProcInstr => + p.text ?= "" + } + } + + property("toString") = { + Prop.forAll { (p: ProcInstr) => + val str = p.toString + Prop.atLeastOne( + str ?= s"", + str ?= s"" + ) + } + } + + property("new(name, \"?>\").throws[Exception]") = { + Prop.throws(classOf[IllegalArgumentException]) { + new ProcInstr("name", "?>") + } + } + + property("new(<>, text).throws[Exception]") = { + Prop.throws(classOf[IllegalArgumentException]) { + new ProcInstr("<>", "text") + } + } + + property("new(xml, text).throws[Exception]") = { + Prop.throws(classOf[IllegalArgumentException]) { + new ProcInstr("xml", "text") + } + } +} diff --git a/shared/src/test/scala/scala/xml/QNodeSpec.scala b/shared/src/test/scala/scala/xml/QNodeSpec.scala new file mode 100644 index 000000000..2ac7e4c16 --- /dev/null +++ b/shared/src/test/scala/scala/xml/QNodeSpec.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } +import org.scalacheck.Prop.AnyOperators + +object QNodeSpec extends CheckProperties("QNode") + with NodeGen { + + property("unapplySeq") = { + Prop.forAll { n: Node => + val res = QNode.unapplySeq(n) + res ?= Some((n.scope.getURI(n.prefix), n.label, n.attributes, n.child.toSeq)) + } + } +} diff --git a/shared/src/test/scala/scala/xml/TextBufferGen.scala b/shared/src/test/scala/scala/xml/TextBufferGen.scala new file mode 100644 index 000000000..45aea1787 --- /dev/null +++ b/shared/src/test/scala/scala/xml/TextBufferGen.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait TextBufferGen extends ArbitraryTextBuffer + with TextGen { + + implicit val arbTextBuffer = Arbitrary { + genTextBuffer + } + + val genTextBuffer: Gen[TextBuffer] = for { + str <- Arbitrary.arbitrary[String] + } yield { + TextBuffer.fromString(str) + } +} diff --git a/shared/src/test/scala/scala/xml/TextBufferSpec.scala b/shared/src/test/scala/scala/xml/TextBufferSpec.scala new file mode 100644 index 000000000..91a53f5a2 --- /dev/null +++ b/shared/src/test/scala/scala/xml/TextBufferSpec.scala @@ -0,0 +1,40 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } + +object TextBufferSpec extends CheckProperties("TextBuffer") + with TextBufferGen { + + // FIXME: Verify result value. + property("fromString") = { + Prop.forAll { str: String => + TextBuffer.fromString(str) + Prop.passed + } + } + + // FIXME: Verify result value. + property("append") = { + Prop.forAll { (tb: TextBuffer, cs: Seq[Char]) => + tb.append(cs) + Prop.passed + } + } + + // FIXME: Verify result value. + property("toText") = { + Prop.forAll { tb: TextBuffer => + tb.toText + Prop.passed + } + } +} diff --git a/shared/src/test/scala/scala/xml/TextGen.scala b/shared/src/test/scala/scala/xml/TextGen.scala new file mode 100644 index 000000000..141254644 --- /dev/null +++ b/shared/src/test/scala/scala/xml/TextGen.scala @@ -0,0 +1,25 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait TextGen extends Utf8StringGen { + + val genText: Gen[Text] = for { + s <- genUtf8String: Gen[String] + } yield { + new Text(s) + } + + implicit val arbText = Arbitrary { + genText + } +} diff --git a/shared/src/test/scala/scala/xml/TextSpec.scala b/shared/src/test/scala/scala/xml/TextSpec.scala new file mode 100644 index 000000000..5adeb9f89 --- /dev/null +++ b/shared/src/test/scala/scala/xml/TextSpec.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } +import org.scalacheck.Prop.AnyOperators + +object TextSpec extends CheckProperties("Text") + with TextGen { + + property("text") = { + Prop.forAll { t: Text => + t.text ?= s"${t.data}" + } + } + + property("unapply") = { + Prop.forAll { t: Text => + Text.unapply(t) ?= Some(t.data) + } + } +} diff --git a/shared/src/test/scala/scala/xml/UnparsedGen.scala b/shared/src/test/scala/scala/xml/UnparsedGen.scala new file mode 100644 index 000000000..d9fb57013 --- /dev/null +++ b/shared/src/test/scala/scala/xml/UnparsedGen.scala @@ -0,0 +1,25 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait UnparsedGen extends Utf8StringGen { + + val genUnparsed: Gen[Unparsed] = for { + s <- genUtf8String: Gen[String] + } yield { + new Unparsed(s) + } + + implicit val arbUnparsed = Arbitrary { + genUnparsed + } +} diff --git a/shared/src/test/scala/scala/xml/Utf8StringGen.scala b/shared/src/test/scala/scala/xml/Utf8StringGen.scala new file mode 100644 index 000000000..1e2fa3c9b --- /dev/null +++ b/shared/src/test/scala/scala/xml/Utf8StringGen.scala @@ -0,0 +1,59 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +!** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait Utf8StringGen { + val genUtf8String: Gen[String] = + Arbitrary.arbitrary[String].map { + _.filter { c => + // 1. SAXParseException: The entity name must immediately follow + // the '&' in the entity reference + // 2. SAXParseException: The content of elements must consist of + // well-formed character data or markup. + "<&".contains(c) == false + }.filter { c => + Character.getType(c) match { + case Character.COMBINING_SPACING_MARK | + Character.CONNECTOR_PUNCTUATION | + // Character.CONTROL | + Character.CURRENCY_SYMBOL | + Character.DASH_PUNCTUATION | + Character.DECIMAL_DIGIT_NUMBER | + Character.ENCLOSING_MARK | + Character.END_PUNCTUATION | + Character.FINAL_QUOTE_PUNCTUATION | + // Character.FORMAT | + Character.INITIAL_QUOTE_PUNCTUATION | + Character.LETTER_NUMBER | + // Character.LINE_SEPARATOR | + Character.LOWERCASE_LETTER | + Character.MATH_SYMBOL | + Character.MODIFIER_LETTER | + Character.MODIFIER_SYMBOL | + Character.NON_SPACING_MARK | + Character.OTHER_LETTER | + Character.OTHER_NUMBER | + Character.OTHER_PUNCTUATION | + Character.OTHER_SYMBOL | + // Character.PARAGRAPH_SEPARATOR | + // Character.PRIVATE_USE | + Character.SPACE_SEPARATOR | + Character.START_PUNCTUATION | + // Character.SURROGATE | + Character.TITLECASE_LETTER | + // Character.UNASSIGNED | + Character.UPPERCASE_LETTER => true + case _ => false + } + } + } +} diff --git a/shared/src/test/scala/scala/xml/Utf8StringSpec.scala b/shared/src/test/scala/scala/xml/Utf8StringSpec.scala new file mode 100644 index 000000000..c873734b9 --- /dev/null +++ b/shared/src/test/scala/scala/xml/Utf8StringSpec.scala @@ -0,0 +1,57 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } + +object Utf8StringSpec extends CheckProperties("Utf8String") + with Utf8StringGen { + + // If character classes change, this will let you know. + property("getType") = { + Prop.forAll { c: Char => + val typ: String = Character.getType(c) match { + case Character.COMBINING_SPACING_MARK => "COMBINING_SPACING_MARK" + case Character.CONNECTOR_PUNCTUATION => "CONNECTOR_PUNCTUATION" + case Character.CONTROL => "CONTROL" + case Character.CURRENCY_SYMBOL => "CURRENCY_SYMBOL" + case Character.DASH_PUNCTUATION => "DASH_PUNCTUATION" + case Character.DECIMAL_DIGIT_NUMBER => "DECIMAL_DIGIT_NUMBER" + case Character.ENCLOSING_MARK => "ENCLOSING_MARK" + case Character.END_PUNCTUATION => "END_PUNCTUATION" + case Character.FINAL_QUOTE_PUNCTUATION => "FINAL_QUOTE_PUNCTUATION" + case Character.FORMAT => "FORMAT" + case Character.INITIAL_QUOTE_PUNCTUATION => "INITIAL_QUOTE_PUNCTUATION" + case Character.LETTER_NUMBER => "LETTER_NUMBER" + case Character.LINE_SEPARATOR => "LINE_SEPARATOR" + case Character.LOWERCASE_LETTER => "LOWERCASE_LETTER" + case Character.MATH_SYMBOL => "MATH_SYMBOL" + case Character.MODIFIER_LETTER => "MODIFIER_LETTER" + case Character.MODIFIER_SYMBOL => "MODIFIER_SYMBOL" + case Character.NON_SPACING_MARK => "NON_SPACING_MARK" + case Character.OTHER_LETTER => "OTHER_LETTER" + case Character.OTHER_NUMBER => "OTHER_NUMBER" + case Character.OTHER_PUNCTUATION => "OTHER_PUNCTUATION" + case Character.OTHER_SYMBOL => "OTHER_SYMBOL" + case Character.PARAGRAPH_SEPARATOR => "PARAGRAPH_SEPARATOR" + case Character.PRIVATE_USE => "PRIVATE_USE" + case Character.SPACE_SEPARATOR => "SPACE_SEPARATOR" + case Character.START_PUNCTUATION => "START_PUNCTUATION" + case Character.SURROGATE => "SURROGATE" + case Character.TITLECASE_LETTER => "TITLECASE_LETTER" + case Character.UNASSIGNED => "UNASSIGNED" + case Character.UPPERCASE_LETTER => "UPPERCASE_LETTER" + } + Prop.collect(s"Character.$typ") { + Prop.passed + } + } + } +} diff --git a/shared/src/test/scala/scala/xml/UtilitySpec.scala b/shared/src/test/scala/scala/xml/UtilitySpec.scala new file mode 100644 index 000000000..5087e68ae --- /dev/null +++ b/shared/src/test/scala/scala/xml/UtilitySpec.scala @@ -0,0 +1,74 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators +import org.scalacheck.Prop.BooleanOperators + +object UtilitySpec extends PropertiesFor("Utility") + with NodeGen { + + val escape = Map( + '<' -> "lt", + '>' -> "gt", + '&' -> "amp", + '"' -> "quot" // , + // '\'' -> "apos" + ) + + property("escape") = { + Prop.forAll { (text: String) => + val sb = new StringBuilder + val res = Utility.escape(text) + val exp = text.iterator.filter { c => + (c >= ' ' || "\n\r\t".contains(c)) + }.foldLeft(sb) { (sb, c) => + escape.get(c).map { str => + sb ++= s"&$str;" + }.getOrElse { + sb += c + } + } + res ?= exp.toString + } + } + + property("escape(\"\\u0000\")") = { + Utility.escape("\u0000") ?= "" + } + + val unescape = escape.map { + case (c, str) => str -> c.toString + } + + property("unescape") = { + Prop.forAll { (text: String) => + val sb = new StringBuilder + val res = Utility.unescape(text, sb) + Prop.atLeastOne( + unescape.contains(text) ==> { + val exp = unescape.get(text).getOrElse { + text + } + res.toString ?= exp + }, + !unescape.contains(text) ==> + (res eq null) + ) + } + } + + property("serialize") = { + Prop.forAll { x: Node => + Utility.serialize(x).toString ?= x.toString + } + } +} diff --git a/shared/src/test/scala/scala/xml/XMLSpec.scala b/shared/src/test/scala/scala/xml/XMLSpec.scala new file mode 100644 index 000000000..3b8338327 --- /dev/null +++ b/shared/src/test/scala/scala/xml/XMLSpec.scala @@ -0,0 +1,38 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen +import org.scalacheck.Prop +import org.scalacheck.{ Properties => CheckProperties } + +object XMLSpec extends CheckProperties("XML") + with NodeGen + with dtd.DocTypeGen { + + def genWriter: Gen[java.io.Writer] = + Gen.const( + new java.io.OutputStreamWriter( + new java.io.ByteArrayOutputStream, + "UTF-8" + ) + ) + + implicit val arbWriter = Arbitrary { + genWriter + } + + property("write") = { + Prop.forAll { (w: java.io.Writer, node: Node, xmlDecl: Boolean, doctype: dtd.DocType) => + XML.write(w, node, "UTF-8", xmlDecl, doctype) + Prop.passed + } + } +} diff --git a/shared/src/test/scala/scala/xml/XmlNameGen.scala b/shared/src/test/scala/scala/xml/XmlNameGen.scala new file mode 100644 index 000000000..7433812ac --- /dev/null +++ b/shared/src/test/scala/scala/xml/XmlNameGen.scala @@ -0,0 +1,49 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait XmlNameGen { + + def isLetter = Utility.isAlpha _ + def isLetterOrDigit = Utility.isAlphaDigit _ + + def validPrefix: Gen[Char] = Gen.oneOf(Gen.alphaChar, Gen.oneOf("_".toSeq)) + def validChar: Gen[Char] = + Gen.oneOf(Gen.alphaNumChar, Gen.oneOf(".-_:".toSeq)) + + val genXmlName: Gen[String] = for { + arbPrefix <- validPrefix + arbName <- Gen.listOf(validChar) + } yield { + arbPrefix.toString + arbName.mkString + } + + def invalidPrefix: Gen[Char] = + Arbitrary.arbitrary[Char].suchThat(!Utility.isNameStart(_)) + + def invalidChar: Gen[Char] = + Arbitrary.arbitrary[Char].suchThat(!Utility.isNameChar(_)) + + def invalidXmlName: Gen[String] = for { + goodPrefix <- validPrefix + goodName <- Gen.listOf(validChar) + badPrefix <- invalidPrefix + badName <- Gen.listOf(invalidChar) + name <- Gen.oneOf( + goodPrefix.toString + badName.mkString, + badPrefix.toString + goodName.mkString, + badPrefix.toString + badName.mkString + ) + } yield { + name + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/ArbitraryDecl.scala b/shared/src/test/scala/scala/xml/dtd/ArbitraryDecl.scala new file mode 100644 index 000000000..8453d4d7f --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/ArbitraryDecl.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ArbitraryDecl { + + val genDecl: Gen[Decl] + + implicit val arbDecl: Arbitrary[Decl] + + val genNotationDecl: Gen[NotationDecl] + + implicit val arbNotationDecl: Arbitrary[NotationDecl] +} diff --git a/shared/src/test/scala/scala/xml/dtd/ContentModelGen.scala b/shared/src/test/scala/scala/xml/dtd/ContentModelGen.scala new file mode 100644 index 000000000..1950b3efe --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/ContentModelGen.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ContentModelGen extends impl.RegExpGen { + + // val genDFAContentModel: Gen[ContentModel] = for { + // r <- Arbitrary.arbitrary[_regexpT] + // d <- Gen.oneOf(MIXED(r), ELEMENTS(r)) + // } yield { + // d + // } + + val genContentModel: Gen[ContentModel] = + Gen.oneOf( + Gen.const(PCDATA), Gen.const(EMPTY), Gen.const(ANY) + // genDFAContentModel + ) + + implicit val arbContentModel = Arbitrary { + genContentModel + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/DTDGen.scala b/shared/src/test/scala/scala/xml/dtd/DTDGen.scala new file mode 100644 index 000000000..4ec7b3d7a --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/DTDGen.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait DTDGen extends DeclGen + with ExternalIDGen { + + def genDTD(sz: Int): Gen[DTD] = for { + extID <- Gen.oneOf( + Gen.const(null), + Arbitrary.arbitrary[ExternalID] + ) + n <- Gen.choose(0, scala.math.sqrt(sz / 2).toInt) + intSubset <- Gen.listOfN(n, Arbitrary.arbitrary[dtd.Decl]) + notationDecls <- Gen.listOfN(n, Arbitrary.arbitrary[NotationDecl]) + } yield { + new DTD { + externalID = extID + decls = intSubset + override def notations = notationDecls + } + } + + implicit def arbDTD = Arbitrary { + Gen.sized(sz => genDTD(sz)) + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/DTDSpec.scala b/shared/src/test/scala/scala/xml/dtd/DTDSpec.scala new file mode 100644 index 000000000..ad5994e9a --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/DTDSpec.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object DTDSpec extends PropertiesFor("dtd.DTD") + with DTDGen { + + property("toString") = { + Prop.forAll { d: DTD => + val str = d.toString + val decls = d.decls.mkString("\n") + Prop.atLeastOne( + str ?= s"DTD [\n${d.externalID}${decls}\n]", + str ?= s"DTD [\n${decls}\n]" + ) + } + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/DeclGen.scala b/shared/src/test/scala/scala/xml/dtd/DeclGen.scala new file mode 100644 index 000000000..71833c616 --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/DeclGen.scala @@ -0,0 +1,176 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait DeclGen extends ArbitraryDecl + with ContentModelGen + with ExternalIDGen + with XmlNameGen + with Utf8StringGen { + + val genDefault: Gen[DefaultDecl] = for { + fixed <- Arbitrary.arbitrary[Boolean] + attValue <- genUtf8String: Gen[String] + } yield { + DEFAULT(fixed, attValue) + } + + val genDefaultDecl: Gen[DefaultDecl] = + Gen.oneOf(Gen.const(REQUIRED), Gen.const(IMPLIED), genDefault) + + val genElemDecl: Gen[ElemDecl] = for { + name <- genXmlName: Gen[String] + contentModel <- Arbitrary.arbitrary[ContentModel] + } yield { + ElemDecl(name, contentModel) + } + + val genAttrDecl: Gen[AttrDecl] = for { + name <- genXmlName: Gen[String] + tpe <- genUtf8String: Gen[String] + default <- Arbitrary.arbitrary[DefaultDecl] + } yield { + AttrDecl(name, s"'$tpe'", default) + } + + val genAttListDecl: Gen[AttListDecl] = for { + name <- genXmlName: Gen[String] + attrs <- Arbitrary.arbitrary[List[AttrDecl]].suchThat(_.nonEmpty) + } yield { + AttListDecl(name, attrs) + } + + val genParsedEntityDecl: Gen[ParsedEntityDecl] = for { + name <- genXmlName: Gen[String] + entdef <- Arbitrary.arbitrary[EntityDef] + } yield { + ParsedEntityDecl(name, entdef) + } + + def genParameterEntityDecl: Gen[ParameterEntityDecl] = for { + name <- genXmlName: Gen[String] + entdef <- Arbitrary.arbitrary[EntityDef] + } yield { + ParameterEntityDecl(name, entdef) + } + + val genUnparsedEntityDecl: Gen[UnparsedEntityDecl] = for { + name <- genXmlName: Gen[String] + entdef <- Arbitrary.arbitrary[EntityDef] + extdef <- Arbitrary.arbitrary[ExternalID] + notation <- genUtf8String: Gen[String] if notation.nonEmpty + } yield { + UnparsedEntityDecl(name, extdef, notation) + } + + def genEntityDecl: Gen[EntityDecl] = + Gen.oneOf( + Arbitrary.arbitrary[ParsedEntityDecl], + Arbitrary.arbitrary[ParameterEntityDecl], + Arbitrary.arbitrary[UnparsedEntityDecl] + ) + + val genNotationDecl: Gen[NotationDecl] = for { + name <- genXmlName: Gen[String] + extdef <- Arbitrary.arbitrary[ExternalID] + } yield { + NotationDecl(name, extdef) + } + + val genIntDef: Gen[IntDef] = for { + value <- genXmlName: Gen[String] + } yield { + IntDef(value) + } + + val genExtDef: Gen[ExtDef] = for { + extID <- Arbitrary.arbitrary[ExternalID] + } yield { + ExtDef(extID) + } + + val genEntityDef: Gen[EntityDef] = + Gen.oneOf(genIntDef, genExtDef) + + val genPEReference: Gen[PEReference] = for { + name <- genXmlName: Gen[String] + } yield { + PEReference(name) + } + + // FIXME: SAXParseException: The markup declarations contained or + // pointed to by the document type declaration must be well-formed. + val genDecl: Gen[Decl] = + genElemDecl + // Gen.oneOf( + // genElemDecl, + // genAttListDecl, + // genEntityDecl + // ) + + implicit val arbDecl = Arbitrary { + genDecl + } + + implicit val arbAttListDecl = Arbitrary { + genAttListDecl + } + + implicit val arbParsedEntityDecl = Arbitrary { + genParsedEntityDecl + } + + implicit val arbParameterEntityDecl = Arbitrary { + genParameterEntityDecl + } + + implicit val arbUnparsedEntityDecl = Arbitrary { + genUnparsedEntityDecl + } + + implicit val arbAttrDecl = Arbitrary { + genAttrDecl + } + + implicit val arbElemDecl = Arbitrary { + genElemDecl + } + + implicit val arbEntityDecl = Arbitrary { + genEntityDecl + } + + implicit val arbNotationDecl = Arbitrary { + genNotationDecl + } + + implicit val arbDefaultDecl = Arbitrary { + genDefaultDecl + } + + implicit val arbEntityDef = Arbitrary { + genEntityDef + } + + implicit val arbIntDef = Arbitrary { + genIntDef + } + + implicit val arbExtDef = Arbitrary { + genExtDef + } + + implicit val arbPEReference = Arbitrary { + genPEReference + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/DeclSpec.scala b/shared/src/test/scala/scala/xml/dtd/DeclSpec.scala new file mode 100644 index 000000000..2b5de6379 --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/DeclSpec.scala @@ -0,0 +1,144 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators + +object DeclSpec extends PropertiesFor("dtd.Decl") + with DeclGen { + + implicit val arbStringBuilder: Arbitrary[StringBuilder] = Arbitrary { + Gen.delay(new StringBuilder) + } + + // FIXME: Expected " + // val str = d.toString + // val attrs = d.attrs.mkString("\n") + // str ?= s"" + // } + // } + + // FIXME: Fix toString above, and delete this buildString test. + property("AttListDecl.buildString") = { + Prop.forAll { (d: AttListDecl, sb: StringBuilder) => + val str = d.buildString(sb).toString + val attrs = d.attrs.mkString("\n") + str ?= s"" + } + } + + property("AttrDecl.toString") = { + Prop.forAll { d: AttrDecl => + d.toString ?= s" ${d.name} ${d.tpe} ${d.default}" + } + } + + property("DefaultDecl.toString") = { + Prop.forAll { d: DefaultDecl => + val str = d.toString + Prop.atLeastOne( + str ?= "#REQUIRED", + str ?= "#IMPLIED", + str.startsWith("#FIXED"), + str.length >= 2 + ) + } + } + + property("ElemDecl.buildString") = { + Prop.forAll { (d: ElemDecl, sb: StringBuilder) => + val str = d.buildString(sb).toString + str.startsWith(s"") + } + } + + property("EntityDecl.buildString") = { + Prop.forAll { (d: EntityDecl, sb: StringBuilder) => + val str = d.buildString(sb).toString + str.startsWith("") + } + } + + property("ParsedEntityDecl.buildString") = { + Prop.forAll { (d: ParsedEntityDecl, sb1: StringBuilder, sb2: StringBuilder) => + val str = d.buildString(sb1).toString + val entdef = d.entdef.buildString(sb2).toString + str ?= s"" + } + } + + property("ParameterEntityDecl.buildString") = { + Prop.forAll { (d: ParameterEntityDecl, sb1: StringBuilder, sb2: StringBuilder) => + val str = d.buildString(sb1).toString + val entdef = d.entdef.buildString(sb2).toString + str ?= s"" + } + } + + property("UnparsedEntityDecl.buildString") = { + Prop.forAll { (d: UnparsedEntityDecl, sb1: StringBuilder, sb2: StringBuilder) => + val str = d.buildString(sb1).toString + val extID = d.extID.buildString(sb2).toString + str ?= s"" + } + } + + property("NotationDecl.buildString") = { + Prop.forAll { (d: NotationDecl, sb: StringBuilder) => + val str = d.buildString(sb).toString + str.startsWith(" + val str = d.buildString(sb).toString + str ?= s"%${d.ent};" + } + } + + property("EntityDef.buildString") = { + Prop.forAll { (d: EntityDef, sb: StringBuilder) => + val str = d.buildString(sb).toString + str.length >= 0 + } + } + + property("IntDef.buildString") = { + Prop.forAll { (d: IntDef, sb: StringBuilder) => + val str = d.buildString(sb).toString + Prop.atLeastOne( + // str ?= "", + str ?= s"'${d.value}'", + str ?= s""""${d.value}"""" + ) + } + } + + property("ExtDef.buildString") = { + Prop.forAll { (d: ExtDef, sb1: StringBuilder, sb2: StringBuilder) => + val str = d.buildString(sb1).toString + val extID = d.extID.buildString(sb2).toString + str ?= extID + } + } + + property("toString") = { + Prop.forAll { d: Decl => + d.toString.length >= 0 + } + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/DocTypeGen.scala b/shared/src/test/scala/scala/xml/dtd/DocTypeGen.scala new file mode 100644 index 000000000..e14d117f2 --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/DocTypeGen.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait DocTypeGen extends DeclGen + with ExternalIDGen + with XmlNameGen { + + def genDocType(sz: Int): Gen[DocType] = for { + name <- genXmlName: Gen[String] + extID <- Arbitrary.arbitrary[ExternalID] + n <- Gen.choose(0, scala.math.sqrt(sz / 2).toInt) + intSubset <- Gen.listOfN(n, Arbitrary.arbitrary[dtd.Decl]) + } yield { + new DocType(name, extID, intSubset) + } + + implicit val arbDocType = Arbitrary { + Gen.sized(sz => genDocType(sz)) + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/DocTypeSpec.scala b/shared/src/test/scala/scala/xml/dtd/DocTypeSpec.scala new file mode 100644 index 000000000..76e30e831 --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/DocTypeSpec.scala @@ -0,0 +1,78 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Prop +import org.scalacheck.{ Properties => PropertiesFor } +import org.scalacheck.Prop.AnyOperators +import org.scalacheck.Prop.BooleanOperators + +object DocTypeSpec extends PropertiesFor("dtd.DocType") + with DocTypeGen + with ExternalIDGen + with DeclGen { + + property("new(name)") = { + Prop.forAll { (name: String) => + Prop.atLeastOne( + Utility.isName(name) ==> { + DocType(name) + Prop.passed + }, + !Utility.isName(name) ==> + Prop.throws(classOf[IllegalArgumentException]) { + DocType(name) + } + ) + } + } + + property("new(name, extId, intSubset)") = { + Prop.forAll { (name: String, extID: ExternalID, intSubset: dtd.Decl) => + Prop.atLeastOne( + Utility.isName(name) ==> { + new DocType(name, extID, Seq(intSubset)) + Prop.passed + }, + !Utility.isName(name) ==> + Prop.throws(classOf[IllegalArgumentException]) { + new DocType(name, extID, Seq(intSubset)) + } + ) + } + } + + property("new(name, extId, emptyInt)") = { + Prop.forAll { (name: String, extID: ExternalID) => + Prop.atLeastOne( + Utility.isName(name) ==> { + new DocType(name, extID, Seq.empty[dtd.Decl]) + Prop.passed + }, + !Utility.isName(name) ==> + Prop.throws(classOf[IllegalArgumentException]) { + new DocType(name, extID, Seq.empty[dtd.Decl]) + } + ) + } + } + + property("toString") = { + Prop.forAll { d: DocType => + val str = d.toString + val intSubset = d.intSubset.mkString + Prop.atLeastOne( + str ?= s"", + str ?= s"", + str ?= s"" + ) + } + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/ExternalIDGen.scala b/shared/src/test/scala/scala/xml/dtd/ExternalIDGen.scala new file mode 100644 index 000000000..5b3354ea1 --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/ExternalIDGen.scala @@ -0,0 +1,66 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd + +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +trait ExternalIDGen extends Utf8StringGen { + + // FIXME: Generate any string of chars, not just alphas. Alphas + // avoid "Gave up after only 0 passed tests. 501 tests were + // discarded." + val genPubIdStr = Gen.alphaStr + .suchThat(_.nonEmpty).suchThat(Utility.checkPubID(_)) + + // FIXME: Generate any string of chars, not just alphas, Alphas + // avoid "Gave up after only 0 passed tests. 501 tests were + // discarded." + val genSysIdStr = Gen.alphaStr + .suchThat(_.nonEmpty).suchThat(Utility.checkSysID(_)) + + val genNonPubIdStr = Arbitrary.arbitrary[String] + .suchThat(!Utility.checkPubID(_)) + + val genNonSysIdStr = Gen.listOf(Gen.oneOf("'", "\"")) + .map(_.mkString).suchThat(!Utility.checkSysID(_)) + + def genExternalID: Gen[ExternalID] = + Gen.oneOf( + Gen.const(NoExternalID), + Arbitrary.arbitrary[PublicID], + Arbitrary.arbitrary[SystemID] + ) + + val genSystemID: Gen[SystemID] = for { + systemId <- genSysIdStr + } yield { + new SystemID(systemId) + } + + val genPublicID: Gen[PublicID] = for { + pubId <- genPubIdStr + systemId <- genSysIdStr + } yield { + new PublicID(pubId, systemId) + } + + implicit val arbSystemID = Arbitrary { + genSystemID + } + + implicit val arbPublicID = Arbitrary { + genPublicID + } + + implicit val arbExternalID = Arbitrary { + genExternalID + } +} diff --git a/shared/src/test/scala/scala/xml/dtd/impl/RegExpGen.scala b/shared/src/test/scala/scala/xml/dtd/impl/RegExpGen.scala new file mode 100644 index 000000000..7b4024f94 --- /dev/null +++ b/shared/src/test/scala/scala/xml/dtd/impl/RegExpGen.scala @@ -0,0 +1,42 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2018, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.xml +package dtd.impl + +// import org.scalacheck.Arbitrary +// import org.scalacheck.Gen + +trait RegExpGen { // extends WordExp { + + // type _labelT = dtd.ContentModel.ElemName + // type _regexpT = RegExp + + // val genLabel: Gen[_labelT] = for { + // name <- Arbitrary.arbitrary[String] + // } yield { + // dtd.ContentModel.ElemName(name) + // } + + // implicit val arbLabel = Arbitrary { + // genLabel + // } + + // val genLetter: Gen[_regexpT] = for { + // label <- Arbitrary.arbitrary[_labelT] + // } yield { + // Letter(label) + // } + + // val genRegExp: Gen[_regexpT] = + // Gen.oneOf(genLetter, Gen.const(Wildcard())) + + // implicit val arbRegExp = Arbitrary { + // genRegExp + // } +}