require 'test_helper'
class ReaderTest < Test::Unit::TestCase
# setup for test
def setup
@src_data = File.readlines(sample_doc_path(:asciidoc_index))
@reader = Asciidoctor::Reader.new @src_data
end
context "has_more_lines?" do
test "returns false for empty document" do
assert !Asciidoctor::Reader.new.has_more_lines?
end
test "returns true with lines remaining" do
assert @reader.has_more_lines?, "Yo, didn't work"
end
end
context "with source data loaded" do
test "get_line returns next line" do
assert_equal @src_data[0], @reader.get_line
end
test "get_line consumes the line it returns" do
reader = Asciidoctor::Reader.new(["foo", "bar"])
_ = reader.get_line
second = reader.get_line
assert_equal "bar", second
end
test "peek_line does not consume the line it returns" do
reader = Asciidoctor::Reader.new(["foo", "bar"])
_ = reader.peek_line
second = reader.peek_line
assert_equal "foo", second
end
test "unshift puts line onto Reader instance for the next get_line" do
reader = Asciidoctor::Reader.new(["foo"])
reader.unshift("bar")
assert_equal "bar", reader.get_line
assert_equal "foo", reader.get_line
end
end
context "Grab lines" do
test "Grab until end" do
input = <<-EOS
This is one paragraph.
This is another paragraph.
EOS
lines = input.lines.entries
reader = Asciidoctor::Reader.new(lines)
result = reader.grab_lines_until
assert_equal 3, result.size
assert_equal lines, result
assert !reader.has_more_lines?
assert reader.empty?
end
test "Grab until blank line" do
input = <<-EOS
This is one paragraph.
This is another paragraph.
EOS
lines = input.lines.entries
reader = Asciidoctor::Reader.new(lines)
result = reader.grab_lines_until :break_on_blank_lines => true
assert_equal 1, result.size
assert_equal lines.first, result.first
assert_equal lines.last, reader.peek_line
end
test "Grab until blank line preserving last line" do
input = <<-EOS
This is one paragraph.
This is another paragraph.
EOS
lines = input.lines.entries
reader = Asciidoctor::Reader.new(lines)
result = reader.grab_lines_until :break_on_blank_lines => true, :preserve_last_line => true
assert_equal 1, result.size
assert_equal lines.first, result.first
assert_equal "\n", reader.peek_line
end
test "Grab until condition" do
input = <<-EOS
--
This is one paragraph inside the block.
This is another paragraph inside the block.
--
This is a paragraph outside the block.
EOS
lines = input.lines.entries
reader = Asciidoctor::Reader.new(lines)
reader.get_line
result = reader.grab_lines_until {|line| line.chomp == '--' }
assert_equal 3, result.size
assert_equal lines[1, 3], result
assert_equal "\n", reader.peek_line
end
test "Grab until condition with last line" do
input = <<-EOS
--
This is one paragraph inside the block.
This is another paragraph inside the block.
--
This is a paragraph outside the block.
EOS
lines = input.lines.entries
reader = Asciidoctor::Reader.new(lines)
reader.get_line
result = reader.grab_lines_until(:grab_last_line => true) {|line| line.chomp == '--' }
assert_equal 4, result.size
assert_equal lines[1, 4], result
assert_equal "\n", reader.peek_line
end
test "Grab until condition with last line and preserving last line" do
input = <<-EOS
--
This is one paragraph inside the block.
This is another paragraph inside the block.
--
This is a paragraph outside the block.
EOS
lines = input.lines.entries
reader = Asciidoctor::Reader.new(lines)
reader.get_line
result = reader.grab_lines_until(:grab_last_line => true, :preserve_last_line => true) {|line| line.chomp == '--' }
assert_equal 4, result.size
assert_equal lines[1, 4], result
assert_equal "--\n", reader.peek_line
end
end
context 'Include Macro' do
test 'include macro is disabled by default and becomes a link' do
input = <<-EOS
include::include-file.asciidoc[]
EOS
para = block_from_string input, :attributes => { 'include-depth' => 0 }
assert_equal 1, para.buffer.size
#assert_equal 'include::include-file.asciidoc[]', para.buffer.join
assert_equal 'link:include-file.asciidoc[include-file.asciidoc]', para.buffer.join
end
test 'include macro is enabled when safe mode is less than SECURE' do
input = <<-EOS
include::fixtures/include-file.asciidoc[]
EOS
doc = document_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
output = doc.render
assert_match(/included content/, output)
end
test 'missing file referenced by include macro does not crash processor' do
input = <<-EOS
include::fixtures/no-such-file.ad[]
EOS
begin
doc = document_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
assert_equal 0, doc.blocks.size
rescue
flunk('include macro should not raise exception on missing file')
end
end
test 'include macro supports line selection' do
input = <<-EOS
include::fixtures/include-file.asciidoc[lines=1;3..4;6..-1]
EOS
output = render_string input, :safe => Asciidoctor::SafeMode::SAFE, :header_footer => false, :attributes => {'docdir' => File.dirname(__FILE__)}
assert_match(/first line/, output)
assert_no_match(/second line/, output)
assert_match(/third line/, output)
assert_match(/fourth line/, output)
assert_no_match(/fifth line/, output)
assert_match(/sixth line/, output)
assert_match(/seventh line/, output)
assert_match(/eighth line/, output)
assert_match(/last line of included content/, output)
end
test 'include macro supports line selection using quoted attribute value' do
input = <<-EOS
include::fixtures/include-file.asciidoc[lines="1, 3..4 , 6 .. -1"]
EOS
output = render_string input, :safe => Asciidoctor::SafeMode::SAFE, :header_footer => false, :attributes => {'docdir' => File.dirname(__FILE__)}
assert_match(/first line/, output)
assert_no_match(/second line/, output)
assert_match(/third line/, output)
assert_match(/fourth line/, output)
assert_no_match(/fifth line/, output)
assert_match(/sixth line/, output)
assert_match(/seventh line/, output)
assert_match(/eighth line/, output)
assert_match(/last line of included content/, output)
end
test 'include macro supports tagged selection' do
input = <<-EOS
include::fixtures/include-file.asciidoc[tags=snippetA;snippetB]
EOS
output = render_string input, :safe => Asciidoctor::SafeMode::SAFE, :header_footer => false, :attributes => {'docdir' => File.dirname(__FILE__)}
assert_match(/snippetA content/, output)
assert_match(/snippetB content/, output)
assert_no_match(/non-tagged content/, output)
assert_no_match(/included content/, output)
end
test 'lines attribute takes precedence over tags attribute in include macro' do
input = <<-EOS
include::fixtures/include-file.asciidoc[lines=1, tags=snippetA;snippetB]
EOS
output = render_string input, :safe => Asciidoctor::SafeMode::SAFE, :header_footer => false, :attributes => {'docdir' => File.dirname(__FILE__)}
assert_match(/first line of included content/, output)
assert_no_match(/snippetA content/, output)
assert_no_match(/snippetB content/, output)
end
test 'indent of included file can be reset to size of indent attribute' do
input = <<-EOS
[source, xml]
----
include::fixtures/basic-docinfo.xml[lines=2..3, indent=0]
----
EOS
output = render_string input, :safe => Asciidoctor::SafeMode::SAFE, :header_footer => false, :attributes => {'docdir' => File.dirname(__FILE__)}
result = xmlnodes_at_xpath('//pre', output, 1).text
assert_equal "2013\nAcme, Inc.", result
end
test "block is called to handle an include macro" do
input = <<-EOS
first line
include::include-file.asciidoc[]
last line
EOS
doc = Asciidoctor::Document.new [], :safe => Asciidoctor::SafeMode::SAFE
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true) {|inc|
":includefile: #{inc}\n\nmiddle line".lines.entries
}
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_match(/^:includefile: include-file.asciidoc$/, lines.join)
end
test 'attributes are substituted in target of include macro' do
input = <<-EOS
:fixturesdir: fixtures
:ext: asciidoc
include::{fixturesdir}/include-file.{ext}[]
EOS
doc = document_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
output = doc.render
assert_match(/included content/, output)
end
test 'line is dropped if target of include macro resolves to empty' do
input = <<-EOS
include::{foodir}/include-file.asciidoc[]
EOS
output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
assert output.strip.empty?
end
test 'line is dropped but not following line if target of include macro resolves to empty' do
input = <<-EOS
include::{foodir}/include-file.asciidoc[]
yo
EOS
output = render_embedded_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => {'docdir' => File.dirname(__FILE__)}
assert_xpath '//p', output, 1
assert_xpath '//p[text()="yo"]', output, 1
end
test 'escaped include macro is left unprocessed' do
input = <<-EOS
\\include::include-file.asciidoc[]
EOS
para = block_from_string input
assert_equal 1, para.buffer.size
assert_equal 'include::include-file.asciidoc[]', para.buffer.join
end
test 'include macro not at start of line is ignored' do
input = <<-EOS
include::include-file.asciidoc[]
EOS
para = block_from_string input
assert_equal 1, para.buffer.size
# NOTE the space gets stripped because the line is treated as an inline literal
assert_equal :literal, para.context
assert_equal 'include::include-file.asciidoc[]', para.buffer.join
end
test 'include macro is disabled when include-depth attribute is 0' do
input = <<-EOS
include::include-file.asciidoc[]
EOS
para = block_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => { 'include-depth' => 0 }
assert_equal 1, para.buffer.size
assert_equal 'include::include-file.asciidoc[]', para.buffer.join
end
test 'include-depth cannot be set by document' do
input = <<-EOS
:include-depth: 1
include::include-file.asciidoc[]
EOS
para = block_from_string input, :safe => Asciidoctor::SafeMode::SAFE, :attributes => { 'include-depth' => 0 }
assert_equal 1, para.buffer.size
assert_equal 'include::include-file.asciidoc[]', para.buffer.join
end
end
context 'build secure asset path' do
test 'allows us to specify a path relative to the current dir' do
doc = Asciidoctor::Document.new
Asciidoctor::Reader.new([], doc, true)
legit_path = Dir.pwd + "/foo"
assert_equal legit_path, doc.normalize_asset_path(legit_path)
end
test "keeps naughty absolute paths from getting outside" do
naughty_path = "#{disk_root}etc/passwd"
doc = Asciidoctor::Document.new
Asciidoctor::Reader.new([], doc, true)
secure_path = doc.normalize_asset_path(naughty_path)
assert naughty_path != secure_path
assert_match(/^#{doc.base_dir}/, secure_path)
end
test "keeps naughty relative paths from getting outside" do
naughty_path = "safe/ok/../../../../../etc/passwd"
doc = Asciidoctor::Document.new
Asciidoctor::Reader.new([], doc, true)
secure_path = doc.normalize_asset_path(naughty_path)
assert naughty_path != secure_path
assert_match(/^#{doc.base_dir}/, secure_path)
end
end
context 'Conditional Inclusions' do
test 'preprocess_next_line returns true if cursor advanced' do
input = <<-EOS
ifdef::asciidoctor[]
Asciidoctor!
endif::asciidoctor[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
assert reader.preprocess_next_line == true
end
test 'preprocess_next_line returns false if cursor not advanced' do
input = <<-EOS
content
ifdef::asciidoctor[]
Asciidoctor!
endif::asciidoctor[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
assert reader.preprocess_next_line == false
end
test 'preprocess_next_line returns nil if cursor advanced past end of source' do
input = <<-EOS
ifdef::foobar[]
swallowed content
endif::foobar[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
assert reader.preprocess_next_line.nil?
end
test 'ifdef with defined attribute includes block' do
input = <<-EOS
ifdef::holygrail[]
There is a holy grail!
endif::holygrail[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'holygrail' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'There is a holy grail!', lines.join.strip
end
test 'ifdef with defined attribute includes text in brackets' do
input = <<-EOS
On our quest we go...
ifdef::holygrail[There is a holy grail!]
There was much rejoicing.
EOS
doc = Asciidoctor::Document.new [], :attributes => {'holygrail' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal "On our quest we go...\nThere is a holy grail!\nThere was much rejoicing.", lines.join.strip
end
test 'ifndef with defined attribute does not include text in brackets' do
input = <<-EOS
On our quest we go...
ifndef::hardships[There is a holy grail!]
There was no rejoicing.
EOS
doc = Asciidoctor::Document.new [], :attributes => {'hardships' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal "On our quest we go...\nThere was no rejoicing.", lines.join.strip
end
test 'include with non-matching nested exclude' do
input = <<-EOS
ifdef::grail[]
holy
ifdef::swallow[]
swallow
endif::swallow[]
grail
endif::grail[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'grail' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal "holy\ngrail", lines.join.strip
end
test 'nested excludes with same condition' do
input = <<-EOS
ifndef::grail[]
ifndef::grail[]
not here
endif::grail[]
endif::grail[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'grail' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal '', lines.join.strip
end
test 'include with nested exclude of inverted condition' do
input = <<-EOS
ifdef::grail[]
holy
ifndef::grail[]
not here
endif::grail[]
grail
endif::grail[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'grail' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal "holy\ngrail", lines.join.strip
end
test 'exclude with matching nested exclude' do
input = <<-EOS
poof
ifdef::swallow[]
no
ifdef::swallow[]
swallow
endif::swallow[]
here
endif::swallow[]
gone
EOS
doc = Asciidoctor::Document.new [], :attributes => {'grail' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal "poof\ngone", lines.join.strip
end
test 'exclude with nested include using shorthand end' do
input = <<-EOS
poof
ifndef::grail[]
no grail
ifndef::swallow[]
or swallow
endif::[]
in here
endif::[]
gone
EOS
doc = Asciidoctor::Document.new [], :attributes => {'grail' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal "poof\ngone", lines.join.strip
end
test 'ifdef with one alternative attribute set includes content' do
input = <<-EOS
ifdef::holygrail,swallow[]
Our quest is complete!
endif::holygrail,swallow[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'swallow' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'Our quest is complete!', lines.join.strip
end
test 'ifdef with no alternative attributes set does not include content' do
input = <<-EOS
ifdef::holygrail,swallow[]
Our quest is complete!
endif::holygrail,swallow[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal '', lines.join.strip
end
test 'ifdef with all required attributes set includes content' do
input = <<-EOS
ifdef::holygrail+swallow[]
Our quest is complete!
endif::holygrail+swallow[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'holygrail' => '', 'swallow' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'Our quest is complete!', lines.join.strip
end
test 'ifdef with missing required attributes does not include content' do
input = <<-EOS
ifdef::holygrail+swallow[]
Our quest is complete!
endif::holygrail+swallow[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'holygrail' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal '', lines.join.strip
end
test 'ifndef with undefined attribute includes block' do
input = <<-EOS
ifndef::holygrail[]
Our quest continues to find the holy grail!
endif::holygrail[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'Our quest continues to find the holy grail!', lines.join.strip
end
test 'ifndef with one alternative attribute set includes content' do
input = <<-EOS
ifndef::holygrail,swallow[]
Our quest is complete!
endif::holygrail,swallow[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'swallow' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'Our quest is complete!', lines.join.strip
end
test 'ifndef with no alternative attributes set includes content' do
input = <<-EOS
ifndef::holygrail,swallow[]
Our quest is complete!
endif::holygrail,swallow[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'Our quest is complete!', lines.join.strip
end
test 'ifndef with any required attributes set does not include content' do
input = <<-EOS
ifndef::holygrail+swallow[]
Our quest is complete!
endif::holygrail+swallow[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'swallow' => ''}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal '', lines.join.strip
end
test 'ifndef with no required attributes set includes content' do
input = <<-EOS
ifndef::holygrail+swallow[]
Our quest is complete!
endif::holygrail+swallow[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'Our quest is complete!', lines.join.strip
end
test 'escaped ifdef is unescaped and ignored' do
input = <<-EOS
\\ifdef::holygrail[]
content
\\endif::holygrail[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal "ifdef::holygrail[]\ncontent\nendif::holygrail[]", lines.join.strip
end
test 'ifeval comparing double-quoted attribute to matching string is included' do
input = <<-EOS
ifeval::["{gem}" == "asciidoctor"]
Asciidoctor it is!
endif::[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'gem' => 'asciidoctor'}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'Asciidoctor it is!', lines.join.strip
end
test 'ifeval comparing single-quoted attribute to matching string is included' do
input = <<-EOS
ifeval::['{gem}' == 'asciidoctor']
Asciidoctor it is!
endif::[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'gem' => 'asciidoctor'}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'Asciidoctor it is!', lines.join.strip
end
test 'ifeval comparing quoted attribute to non-matching string is ignored' do
input = <<-EOS
ifeval::['{gem}' == 'asciidoctor']
Asciidoctor it is!
endif::[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'gem' => 'tilt'}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal '', lines.join.strip
end
test 'ifeval comparing attribute to lower version number is included' do
input = <<-EOS
ifeval::['{asciidoctor-version}' >= '0.1.0']
That version will do!
endif::[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'That version will do!', lines.join.strip
end
test 'ifeval comparing attribute to self is included' do
input = <<-EOS
ifeval::['{asciidoctor-version}' == '{asciidoctor-version}']
Of course it's the same!
endif::[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'Of course it\'s the same!', lines.join.strip
end
test 'ifeval arguments can be mirrored' do
input = <<-EOS
ifeval::["0.1.0" <= "{asciidoctor-version}"]
That version will do!
endif::[]
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'That version will do!', lines.join.strip
end
test 'ifeval matching numeric comparison is included' do
input = <<-EOS
ifeval::[{rings} == 1]
One ring to rule them all!
endif::[]
EOS
doc = Asciidoctor::Document.new [], :attributes => {'rings' => 1}
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal 'One ring to rule them all!', lines.join.strip
end
test 'ifdef with no target is ignored' do
input = <<-EOS
ifdef::[]
content
EOS
doc = Asciidoctor::Document.new
reader = Asciidoctor::Reader.new(input.lines.entries, doc, true)
lines = []
while reader.has_more_lines?
lines << reader.get_line
end
assert_equal "ifdef::[]\ncontent", lines.join.strip
end
end
context 'Text processing' do
test 'should clean CRLF from end of lines' do
input = <<-EOS
source\r
with\r
CRLF\r
endlines\r
EOS
reader = Asciidoctor::Reader.new(input.lines.entries, Asciidoctor::Document.new, true)
reader.lines.each do |line|
assert !line.end_with?("\r\n")
end
end
test 'sanitize attribute name' do
assert_equal 'foobar', @reader.sanitize_attribute_name("Foo Bar")
assert_equal 'foo', @reader.sanitize_attribute_name("foo")
assert_equal 'foo3-bar', @reader.sanitize_attribute_name("Foo 3^ # - Bar[")
end
end
end