summaryrefslogtreecommitdiff
path: root/test/reader_test.rb
blob: 49eacbf05a8294d22db849f2caa1be2acccb1623 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
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_lines?" do
    test "returns false for empty document" do
      assert !Asciidoctor::Reader.new.has_lines?
    end

    test "returns true with lines remaining" do
      assert @reader.has_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_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' 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
    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 "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
      Asciidoctor::Reader.new(input.lines.entries, doc) {|inc|
        ":file: #{inc}\n\nmiddle line".lines.entries
      }
      assert_equal 'include-file.asciidoc', doc.attributes['file']
    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
      reader = Asciidoctor::Reader.new(["foo"], doc)
      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 = "/etc/passwd"
      doc = Asciidoctor::Document.new
      reader = Asciidoctor::Reader.new(["foo"], doc)
      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
      reader = Asciidoctor::Reader.new(["foo"], doc)
      secure_path = doc.normalize_asset_path(naughty_path)
      assert naughty_path != secure_path
      assert_match(/^#{doc.base_dir}/, secure_path)
    end
  end

  # TODO these tests could be expanded
  context 'Conditional blocks' do
    test 'ifdef with defined attribute includes block' do
      input = <<-EOS
:holygrail:

ifdef::holygrail[]
There is a holy grail!
endif::holygrail[]
      EOS
       
      reader = Asciidoctor::Reader.new(input.lines.entries, Asciidoctor::Document.new)
      assert_match(/There is a holy grail!/, reader.lines.join)
    end

    test 'ifndef with undefined attribute includes block' do
      input = <<-EOS
ifndef::holygrail[]
Our quest continues to find the holy grail!
endif::holygrail[]
      EOS

      reader = Asciidoctor::Reader.new(input.lines.entries, Asciidoctor::Document.new)
      assert_match(/Our quest continues to find the holy grail!/, reader.lines.join)
    end
  end

  context 'Text processing' do
    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