///|
using @qc {type Gen}
///|
impl @qc.Shrink for XMLDocument with shrink(self) {
[].iter()
}
///|
impl @quickcheck.Arbitrary for XMLDocument with 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 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 : @qc.Gen[Array[String]] = Gen::spawn()
.such_that(is_value)
.array_with_size(size)
let attributes = iterGen
.run(size, rs)
.fold(init=Map::new(), fn(map, value) {
map.set("key_\{map.length()+1}", value)
map
})
let childrenGen : @qc.Gen[Iter[XMLChildren]] = Gen::spawn()
let children = childrenGen
.run(size, rs)
.fold(init=[], fn(queue, child) {
queue.push(child)
queue
})
let emptyGen : @qc.Gen[Bool] = @qc.one_of([@qc.pure(true), @qc.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 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::new(fn(size, rs) {
let charGen : @qc.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] = @qc.one_of([
@qc.pure(" "),
@qc.pure("\t"),
@qc.pure("\r"),
@qc.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] = @qc.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(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)
}