///|
using @gen {type Gen}
///|
impl @qc.Shrink for XMLDocument with fn shrink(self) {
[].iter()
}
///|
impl @quickcheck.Arbitrary for XMLDocument with fn arbitrary(size, rs) {
{
version: "1.0",
encoding: "UTF-8",
standalone: true,
dtd: None,
root: Gen::spawn().run(size, rs),
}
}
///|
impl @quickcheck.Arbitrary for XMLElement with fn arbitrary(size, rs) {
let charGen : Gen[Char] = Gen::spawn()
let name = charGen.such_that(is_name_start_char).run(size, rs).to_string() +
charGen
.such_that(is_name_char)
.array_with_size(size - 1)
.run(size - 1, rs)
.iter()
.fold(init="", fn(s, c) { s + c.to_string() })
fn is_value(s : String) -> Bool {
!(s.contains("\"") || s.contains("<") || s.contains("&"))
}
let iterGen : Gen[Array[String]] = Gen::spawn()
.such_that(is_value)
.array_with_size(size)
let attributes = iterGen
.run(size, rs)
.fold(init=Map([]), fn(map, value) {
map.set("key_\{map.length()+1}", value)
map
})
let childrenGen : Gen[Iter[XMLChildren]] = Gen::spawn()
let children = childrenGen
.run(size, rs)
.fold(init=[], fn(queue, child) {
queue.push(child)
queue
})
let emptyGen : Gen[Bool] = @gen.one_of([@gen.pure(true), @gen.pure(false)])
let empty_element = if children.is_empty() {
emptyGen.run(size, rs)
} else {
false
}
{ name, empty_element, attributes, children }
}
///|
impl @quickcheck.Arbitrary for XMLChildren with fn arbitrary(size, rs) {
fn valid_string(s : String) -> Bool {
s.iter().all(fn(c) { is_char(c) })
}
let strGen : Gen[String] = Gen::spawn().such_that(valid_string)
let refGen : Gen[String] = Gen(fn(size, rs) {
let charGen : Gen[Char] = Gen::spawn()
let name = charGen.such_that(is_name_start_char).run(size, rs).to_string() +
charGen
.such_that(is_name_char)
.array_with_size(size - 1)
.run(size - 1, rs)
.iter()
.fold(init="", fn(s, c) { s + c.to_string() })
"&" + name + ";"
})
let wsGen : Gen[String] = @gen.one_of([
@gen.pure(" "),
@gen.pure("\t"),
@gen.pure("\r"),
@gen.pure("\n"),
])
.array_with_size(size)
.fmap(arr => arr.join(""))
let elementGen : Gen[XMLElement] = Gen::spawn()
//Element(XMLElement)
//Reference(String)
//CDATA(String)
//PI(String)
//Comment(String)
//Text(String)
//WhiteSpace(String)
// TODO : 按照实际的内容生成
fn is_text(s : String) -> Bool {
!(s.contains("&") || s.contains("<"))
}
fn is_cdata(s : String) -> Bool {
!s.contains("]]>")
}
fn is_PI(s : String) -> Bool {
!s.contains("?>")
}
fn is_comment(s : String) -> Bool {
!s.has_suffix("-") && !s.contains("-->")
}
let childGen : Gen[XMLChildren] = @gen.one_of([
refGen.fmap(fn(s) { Reference(s) }),
strGen.such_that(is_cdata).fmap(fn(s) { CDATA(s) }),
strGen.such_that(is_PI).fmap(fn(s) { XMLChildren::PI(s) }),
strGen.such_that(is_comment).fmap(fn(s) { XMLChildren::Comment(s) }),
strGen.such_that(is_text).fmap(fn(s) { Text(s) }),
wsGen.fmap(fn(s) { XMLChildren::WhiteSpace(s) }),
elementGen.fmap(fn(e) { Element(e) }).resize(size - 1), // limit nesting depth
])
childGen.run(size, rs)
}
///|
fn trivial(xml : XMLDocument) -> Bool {
let s = xml.to_string()
let xml2 = xml_from_string(s)
!(xml2 is None)
}
///|
fn trivial_iter(xml : XMLDocument) -> Bool {
let s = xml.to_string()
let xml2 = xml_from_iter(s.iter())
!(xml2 is None)
}
///|
fn trivial_ctx(xml : XMLDocument) -> Bool {
let s = xml.to_string()
let (xml2, ctx) = xml_from_string_with_ctx(s)
if !ctx.errors.is_empty() {
println(Repr(ctx.errors))
}
let flag = ctx.errors
.filter(e => {
match e.kind {
InternalParserError => true
_ => false
}
})
.is_empty()
if xml2 is None {
return false
}
flag
}
///|
test "fuzzing" {
@qc.quick_check_fn(trivial)
@qc.quick_check_fn(trivial_iter)
@qc.quick_check_fn(trivial_ctx)
}