require "spec"
require "xml"
private def xml
<<-XML
John
Peter
XML
end
module XML
describe Reader do
describe ".new" do
context "with default parser options" do
it "can be initialized from a string" do
reader = Reader.new(xml)
reader.should be_a(XML::Reader)
reader.read.should be_true
reader.name.should eq("people")
reader.read.should be_true
reader.name.should eq("#text")
end
it "can be initialized from an io" do
io = IO::Memory.new(xml)
reader = Reader.new(io)
reader.should be_a(XML::Reader)
reader.read.should be_true
reader.name.should eq("people")
reader.read.should be_true
reader.name.should eq("#text")
end
end
context "with custom parser options" do
it "can be initialized from a string" do
reader = Reader.new(xml, XML::ParserOptions::NOBLANKS)
reader.should be_a(XML::Reader)
reader.read.should be_true
reader.name.should eq("people")
reader.read.should be_true
reader.name.should eq("person")
end
it "can be initialized from an io" do
io = IO::Memory.new(xml)
reader = Reader.new(io, XML::ParserOptions::NOBLANKS)
reader.should be_a(XML::Reader)
reader.read.should be_true
reader.name.should eq("people")
reader.read.should be_true
reader.name.should eq("person")
end
end
end
describe "#read" do
it "reads all nodes" do
reader = Reader.new(xml)
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("people")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("person")
reader["id"].should eq("1")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("name")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::TEXT)
reader.name.should eq("#text")
reader.value.should eq("John")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("name")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("person")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("person")
reader["id"].should eq("2")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("name")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::TEXT)
reader.name.should eq("#text")
reader.value.should eq("Peter")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("name")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("person")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("people")
reader.read.should be_false
end
it "reads all non-blank nodes with NOBLANKS option" do
reader = Reader.new(xml, XML::ParserOptions::NOBLANKS)
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("people")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("person")
reader["id"].should eq("1")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("name")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::TEXT)
reader.name.should eq("#text")
reader.value.should eq("John")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("name")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("person")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("person")
reader["id"].should eq("2")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("name")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::TEXT)
reader.name.should eq("#text")
reader.value.should eq("Peter")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("name")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("person")
reader.read.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("people")
reader.read.should be_false
end
end
describe "#next" do
it "reads next node in doc order, skipping subtrees" do
reader = Reader.new(xml)
while reader.read
break if reader.depth == 2
end
reader.next.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("name")
reader.next.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.next.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("person")
reader["id"].should eq("1")
reader.next.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.next.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("person")
reader["id"].should eq("2")
reader.next.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.next.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("people")
reader.next.should be_false
end
end
describe "#next_sibling" do
it "reads next sibling node in doc order, skipping subtrees" do
reader = Reader.new(xml)
while reader.read
break if reader.depth == 1
end
reader.next_sibling.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("person")
reader["id"].should eq("1")
reader.next_sibling.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.next_sibling.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("person")
reader["id"].should eq("2")
reader.next_sibling.should be_true
reader.node_type.should eq(XML::Reader::Type::SIGNIFICANT_WHITESPACE)
reader.name.should eq("#text")
reader.next_sibling.should be_false
end
end
describe "#node_type" do
it "returns the node type" do
reader = Reader.new("")
reader.node_type.should eq(XML::Reader::Type::NONE)
reader.read
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
end
end
describe "#name" do
it "reads node name" do
reader = Reader.new("")
reader.name.should eq("")
reader.read
reader.name.should eq("root")
end
end
describe "#empty_element?" do
it "checks if the node is empty" do
reader = Reader.new("")
reader.empty_element?.should be_false
reader.read
reader.empty_element?.should be_true
reader = Reader.new("")
reader.read
reader.empty_element?.should be_false
end
end
describe "#has_attributes?" do
it "checks if the node has attributes" do
reader = Reader.new(%{})
reader.has_attributes?.should be_false
reader.read #
reader.has_attributes?.should be_true
reader.read #
reader.has_attributes?.should be_false
reader.read #
reader.has_attributes?.should be_true
end
end
describe "#attributes_count" do
it "returns the node's number of attributes" do
reader = Reader.new(%{})
reader.attributes_count.should eq(0)
reader.read #
reader.attributes_count.should eq(1)
reader.read #
reader.attributes_count.should eq(0)
reader.read #
# This is weird, since has_attributes? will be true.
reader.attributes_count.should eq(0)
end
end
describe "#move_to_first_attribute" do
it "moves to the first attribute of the node" do
reader = Reader.new(%{})
reader.move_to_first_attribute.should be_false
reader.read #
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.move_to_first_attribute.should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id")
reader.value.should eq("1")
reader.read #
reader.move_to_first_attribute.should be_false
reader.read #
reader.move_to_first_attribute.should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id")
reader.value.should eq("1")
reader.read.should be_false
end
end
describe "#move_to_next_attribute" do
it "moves to the next attribute of the node" do
reader = Reader.new(%{})
reader.move_to_next_attribute.should be_false
reader.read #
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.move_to_next_attribute.should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id")
reader.value.should eq("1")
reader.move_to_next_attribute.should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id2")
reader.value.should eq("2")
reader.move_to_next_attribute.should be_false
reader.read #
reader.move_to_next_attribute.should be_false
reader.read #
reader.move_to_next_attribute.should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id")
reader.value.should eq("1")
reader.read.should be_false
end
end
describe "#move_to_attribute" do
it "moves to attribute with the specified name" do
reader = Reader.new(%{})
reader.move_to_attribute("id2").should be_false
reader.read #
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.move_to_attribute("id2").should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id2")
reader.value.should eq("2")
reader.move_to_attribute("id").should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id")
reader.value.should eq("1")
reader.move_to_attribute("bogus").should be_false
reader.read #
reader.move_to_attribute("id2").should be_false
reader.read #
reader.move_to_attribute("id2").should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id2")
reader.value.should eq("2")
reader.read.should be_false
end
it "raises if attribute contains null byte" do
reader = Reader.new("")
expect_raises(Exception) { reader.move_to_attribute("\0") }
end
end
describe "#[]" do
it "reads node attributes" do
reader = Reader.new("")
expect_raises(KeyError) { reader["id"] }
reader.read
expect_raises(KeyError) { reader["id"] }
reader = Reader.new(%{})
reader.read
reader["id"].should eq("1")
reader = Reader.new(%{})
reader.read #
reader["id"].should eq("1")
reader.read #
expect_raises(KeyError) { reader["id"] }
reader.read #
reader["id"].should eq("1")
end
it "raises if attribute contains null byte" do
reader = Reader.new("")
expect_raises(Exception) { reader["\0"] }
end
end
describe "#[]?" do
it "reads node attributes" do
reader = Reader.new("")
reader["id"]?.should be_nil
reader.read
reader["id"]?.should be_nil
reader = Reader.new(%{})
reader.read
reader["id"]?.should eq("1")
reader = Reader.new(%{})
reader.read #
reader["id"]?.should eq("1")
reader.read #
reader["id"]?.should be_nil
reader.read #
reader["id"]?.should eq("1")
end
it "raises if attribute contains null byte" do
reader = Reader.new("")
expect_raises(Exception) { reader["\0"]? }
end
end
describe "#move_to_element" do
it "moves to the element node that contains the current attribute node" do
reader = Reader.new(%{})
reader.move_to_element.should be_false
reader.read #
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("root")
reader.move_to_element.should be_false
reader.move_to_first_attribute.should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id")
reader.move_to_element.should be_true
reader.node_type.should eq(XML::Reader::Type::ELEMENT)
reader.name.should eq("root")
reader.read #
reader.move_to_element.should be_false
reader.move_to_first_attribute.should be_true
reader.node_type.should eq(XML::Reader::Type::ATTRIBUTE)
reader.name.should eq("id")
reader.move_to_element.should be_true
reader.node_type.should eq(XML::Reader::Type::END_ELEMENT)
reader.name.should eq("root")
reader.read.should be_false
end
end
describe "#depth" do
it "returns the depth of the node" do
reader = Reader.new("")
reader.depth.should eq(0)
reader.read #
reader.depth.should eq(0)
reader.read #
reader.depth.should eq(1)
reader.read #
reader.depth.should eq(0)
end
end
describe "#read_inner_xml" do
it "reads the contents of the node including child nodes and markup" do
reader = Reader.new("\n\n\n")
reader.read_inner_xml.should eq("")
reader.read #
reader.read_inner_xml.should eq("\n\n")
reader.read # \n
reader.read_inner_xml.should eq("")
reader.read #
reader.read_inner_xml.should eq("")
reader.read # \n
reader.read_inner_xml.should eq("")
reader.read #
reader.read_inner_xml.should eq("")
reader.read.should be_false
end
end
describe "#read_outer_xml" do
it "reads the xml of the node including child nodes and markup" do
reader = Reader.new("\n\n\n")
reader.read_outer_xml.should eq("")
reader.read #
reader.read_outer_xml.should eq("\n\n")
reader.read # \n
reader.read_outer_xml.should eq("\n")
reader.read #
reader.read_outer_xml.should eq("")
reader.read # \n
reader.read_outer_xml.should eq("\n")
reader.read #
# Note that the closing element is transformed into a self-closing one.
reader.read_outer_xml.should eq("")
reader.read.should be_false
end
end
describe "#expand" do
it "raises an exception if the node could not be expanded" do
reader = Reader.new(%{})
reader.read #
node = reader.expand
node.should be_a(XML::Node)
node.attributes["id"].content.should eq("1")
node.xpath_node("child").should be_a(XML::Node)
end
it "is only available until the next read" do
reader = Reader.new(%{})
reader.read #
reader.read #
node = reader.expand
node.should be_a(XML::Node)
node.xpath_node("subchild").should be_a(XML::Node)
reader.read #
reader.read #
node.xpath_node("subchild").should be_nil
end
end
describe "#expand?" do
it "parses the content of the node and subtree" do
reader = Reader.new(%{})
reader.expand?.should be_nil
reader.read #
node = reader.expand?
node.should be_a(XML::Node)
node.not_nil!.attributes["id"].content.should eq("1")
node.not_nil!.xpath_node("child").should be_a(XML::Node)
end
it "is only available until the next read" do
reader = Reader.new(%{})
reader.read #
reader.read #
node = reader.expand?
node.should be_a(XML::Node)
node.not_nil!.xpath_node("subchild").should be_a(XML::Node)
reader.read #
reader.read #
node.not_nil!.xpath_node("subchild").should be_nil
end
end
describe "#value" do
it "reads node text value" do
reader = Reader.new(%{hello})
reader.value.should eq("")
reader.read #
reader.value.should eq("")
reader.read # hello
reader.value.should eq("hello")
reader.read #
reader.value.should eq(" world ")
reader.read #
reader.move_to_first_attribute.should be_true
reader.value.should eq("1")
end
end
describe "#to_unsafe" do
it "returns a pointer to the underlying LibXML::XMLTextReader" do
reader = Reader.new("")
reader.to_unsafe.should be_a(LibXML::XMLTextReader)
end
end
describe "#errors" do
it "makes errors accessible" do
options = XML::ParserOptions::RECOVER | XML::ParserOptions::NONET
reader = XML::Reader.new(%(), options)
reader.read
reader.expand?
reader.errors.map(&.to_s).should eq ["Opening and ending tag mismatch: people line 1 and foo"]
end
end
end
end