1require 'rdoc/test_case'
2
3class TestRDocServlet < RDoc::TestCase
4
5  def setup
6    super
7
8    @orig_gem_path = Gem.path
9
10    @tempdir = File.join Dir.tmpdir, "test_rdoc_servlet_#{$$}"
11    Gem.use_paths @tempdir
12    Gem.ensure_gem_subdirectories @tempdir
13
14    @spec = Gem::Specification.new 'spec', '1.0'
15    @spec.loaded_from = @spec.spec_file
16
17    Gem::Specification.reset
18    Gem::Specification.all = [@spec]
19
20    @server = {}
21    def @server.mount(*) end
22
23    @stores = {}
24    @cache  = Hash.new { |hash, store| hash[store] = {} }
25
26    @s = RDoc::Servlet.new @server, @stores, @cache
27
28    @req = WEBrick::HTTPRequest.new :Logger => nil
29    @res = WEBrick::HTTPResponse.new :HTTPVersion => '1.0'
30
31    def @req.path= path
32      instance_variable_set :@path, path
33    end
34
35    @req.instance_variable_set :@header, Hash.new { |h, k| h[k] = [] }
36
37    @base        = File.join @tempdir, 'base'
38    @system_dir  = File.join @tempdir, 'base', 'system'
39    @home_dir    = File.join @tempdir, 'home'
40    @gem_doc_dir = File.join @tempdir, 'doc'
41
42    @orig_base = RDoc::RI::Paths::BASE
43    RDoc::RI::Paths::BASE.replace @base
44    @orig_ri_path_homedir = RDoc::RI::Paths::HOMEDIR
45    RDoc::RI::Paths::HOMEDIR.replace @home_dir
46
47    RDoc::RI::Paths.instance_variable_set \
48      :@gemdirs, %w[/nonexistent/gems/example-1.0/ri]
49  end
50
51  def teardown
52    super
53
54    Gem.use_paths(*@orig_gem_path)
55    Gem::Specification.reset
56
57    FileUtils.rm_rf @tempdir
58
59    RDoc::RI::Paths::BASE.replace @orig_base
60    RDoc::RI::Paths::HOMEDIR.replace @orig_ri_path_homedir
61    RDoc::RI::Paths.instance_variable_set :@gemdirs, nil
62  end
63
64  def test_asset
65    temp_dir do
66      now = Time.now
67
68      open 'rdoc.css', 'w' do |io| io.write 'h1 { color: red }' end
69      File.utime now, now, 'rdoc.css'
70
71      @s.asset_dirs[:darkfish] = '.'
72
73      @req.path = 'rdoc.css'
74
75      @s.asset :darkfish, @req, @res
76
77      assert_equal 'h1 { color: red }', @res.body
78      assert_equal 'text/css',          @res.content_type
79      assert_equal now.httpdate,        @res['last-modified']
80    end
81  end
82
83  def test_do_GET
84    touch_system_cache_path
85
86    @req.path = '/ruby/Missing.html'
87
88    @s.do_GET @req, @res
89
90    assert_equal 404, @res.status
91  end
92
93  def test_do_GET_asset_darkfish
94    temp_dir do
95      FileUtils.touch 'rdoc.css'
96
97      @s.asset_dirs[:darkfish] = '.'
98
99      @req.path = '/rdoc.css'
100
101      @s.do_GET @req, @res
102
103      assert_equal 'text/css',          @res.content_type
104    end
105  end
106
107  def test_do_GET_asset_json_index
108    temp_dir do
109      FileUtils.mkdir 'js'
110      FileUtils.touch 'js/navigation.js'
111
112      @s.asset_dirs[:json_index] = '.'
113
114      @req.path = '/js/navigation.js'
115
116      @s.do_GET @req, @res
117
118      assert_equal 'application/javascript', @res.content_type
119    end
120  end
121
122  def test_do_GET_error
123    touch_system_cache_path
124
125    def @req.path() raise 'no' end
126
127    @s.do_GET @req, @res
128
129    assert_equal 500, @res.status
130  end
131
132  def test_do_GET_mount_path
133    @s = RDoc::Servlet.new @server, @stores, @cache, '/mount/path'
134
135    temp_dir do
136      FileUtils.touch 'rdoc.css'
137
138      @s.asset_dirs[:darkfish] = '.'
139
140      @req.path = '/mount/path/rdoc.css'
141
142      @s.do_GET @req, @res
143
144      assert_equal 'text/css', @res.content_type
145    end
146  end
147
148  def test_do_GET_not_modified
149    touch_system_cache_path
150    @req.header['if-modified-since'] = [(Time.now + 10).httpdate]
151    @req.path = '/ruby/Missing.html'
152
153    assert_raises WEBrick::HTTPStatus::NotModified do
154      @s.do_GET @req, @res
155    end
156  end
157
158  def test_do_GET_root
159    touch_system_cache_path
160
161    @req.path = '/'
162
163    @s.do_GET @req, @res
164
165    assert_equal 'text/html',                                 @res.content_type
166    assert_match %r%<title>Local RDoc Documentation</title>%, @res.body
167  end
168
169  def test_do_GET_root_search
170    touch_system_cache_path
171
172    @req.path = '/js/search_index.js'
173
174    @s.do_GET @req, @res
175
176    assert_equal 'application/javascript', @res.content_type
177  end
178
179  def test_documentation_page_class
180    store = RDoc::Store.new
181
182    generator = @s.generator_for store
183
184    file      = store.add_file 'file.rb'
185    klass     = file.add_class RDoc::NormalClass, 'Klass'
186                klass.add_class RDoc::NormalClass, 'Sub'
187
188    @s.documentation_page store, generator, 'Klass::Sub.html', @req, @res
189
190    assert_match %r%<title>class Klass::Sub - </title>%, @res.body
191    assert_match %r%<body id="top" class="class">%,      @res.body
192  end
193
194  def test_documentation_page_not_found
195    store = RDoc::Store.new
196
197    generator = @s.generator_for store
198
199    @req.path = '/ruby/Missing.html'
200
201    @s.documentation_page store, generator, 'Missing.html', @req, @res
202
203    assert_equal 404, @res.status
204  end
205
206  def test_documentation_page_page
207    store = RDoc::Store.new
208
209    generator = @s.generator_for store
210
211    readme = store.add_file 'README.rdoc'
212    readme.parser = RDoc::Parser::Simple
213
214    @s.documentation_page store, generator, 'README_rdoc.html', @req, @res
215
216    assert_match %r%<title>README - </title>%, @res.body
217    assert_match %r%<body class="file">%,      @res.body
218  end
219
220  def test_documentation_source
221    store, path = @s.documentation_source '/ruby/Object.html'
222
223    assert_equal @system_dir, store.path
224
225    assert_equal 'Object.html', path
226  end
227
228  def test_documentation_source_cached
229    cached_store = RDoc::Store.new
230
231    @stores['ruby'] = cached_store
232
233    store, path = @s.documentation_source '/ruby/Object.html'
234
235    assert_same cached_store, store
236
237    assert_equal 'Object.html', path
238  end
239
240  def test_error
241    e = RuntimeError.new 'foo'
242    e.set_backtrace caller
243
244    @s.error e, @req, @res
245
246    assert_equal 'text/html',      @res.content_type
247    assert_equal 500,              @res.status
248    assert_match %r%<title>Error%, @res.body
249  end
250
251  def test_generator_for
252    store = RDoc::Store.new
253    store.main  = 'MAIN_PAGE.rdoc'
254    store.title = 'Title'
255
256    generator = @s.generator_for store
257
258    refute generator.file_output
259
260    assert_equal '..', generator.asset_rel_path
261
262    assert_equal 'MAIN_PAGE.rdoc', @s.options.main_page
263    assert_equal 'Title',          @s.options.title
264
265    assert_kind_of RDoc::RDoc, store.rdoc
266    assert_same generator, store.rdoc.generator
267  end
268
269  def test_if_modified_since
270    skip 'File.utime on directory not supported' if Gem.win_platform?
271
272    temp_dir do
273      now = Time.now
274      File.utime now, now, '.'
275
276      @s.if_modified_since @req, @res, '.'
277
278      assert_equal now.to_i, Time.parse(@res['last-modified']).to_i
279    end
280  end
281
282  def test_if_modified_since_not_modified
283    skip 'File.utime on directory not supported' if Gem.win_platform?
284
285    temp_dir do
286      now = Time.now
287      File.utime now, now, '.'
288
289      @req.header['if-modified-since'] = [(now + 10).httpdate]
290
291      assert_raises WEBrick::HTTPStatus::NotModified do
292        @s.if_modified_since @req, @res, '.'
293      end
294
295      assert_equal now.to_i, Time.parse(@res['last-modified']).to_i
296    end
297  end
298
299  def test_installed_docs
300    touch_system_cache_path
301
302    expected = [
303      ['Ruby Documentation', 'ruby/', true,  :system,
304        @system_dir],
305      ['Site Documentation', 'site/', false, :site,
306        File.join(@base, 'site')],
307      ['Home Documentation', 'home/', false, :home,
308        RDoc::RI::Paths::HOMEDIR],
309      ['spec-1.0', 'spec-1.0/',       false, :gem,
310        File.join(@spec.doc_dir, 'ri')],
311    ]
312
313    assert_equal expected, @s.installed_docs
314  end
315
316  def test_not_found
317    generator = @s.generator_for RDoc::Store.new
318
319    @req.path = '/ruby/Missing.html'
320
321    @s.not_found generator, @req, @res
322
323    assert_equal 404,                                @res.status
324    assert_match %r%<title>Not Found</title>%,       @res.body
325    assert_match %r%<kbd>/ruby/Missing\.html</kbd>%, @res.body
326  end
327
328  def test_ri_paths
329    paths = @s.ri_paths
330
331    expected = [
332      [@system_dir,                    :system],
333      [File.join(@base, 'site'),       :site],
334      [RDoc::RI::Paths::HOMEDIR,       :home],
335      [File.join(@spec.doc_dir, 'ri'), :gem],
336    ]
337
338    assert_equal expected, paths.to_a
339  end
340
341  def test_root
342    @s.root @req, @res
343
344    assert_equal 'text/html',                                 @res.content_type
345    assert_match %r%<title>Local RDoc Documentation</title>%, @res.body
346  end
347
348  def test_root_search
349    touch_system_cache_path
350
351    @s.root_search @req, @res
352
353    assert_equal 'application/javascript', @res.content_type
354
355    @res.body =~ /\{.*\}/
356
357    index = JSON.parse $&
358
359    expected = {
360      'index' => {
361        'searchIndex' => %w[
362          Ruby\ Documentation
363        ],
364        'longSearchIndex' => %w[
365          Ruby\ Documentation
366        ],
367        'info' => [
368          ['Ruby Documentation', '', 'ruby', '',
369            'Documentation for the Ruby standard library'],
370        ],
371      }
372    }
373
374    assert_equal expected, index
375  end
376
377  def test_show_documentation_index
378    touch_system_cache_path
379
380    @req.path = '/ruby'
381
382    @s.show_documentation @req, @res
383
384    assert_equal 'text/html',                               @res.content_type
385    assert_match %r%<title>Standard Library Documentation%, @res.body
386  end
387
388  def test_show_documentation_table_of_contents
389    touch_system_cache_path
390
391    @req.path = '/ruby/table_of_contents.html'
392
393    @s.show_documentation @req, @res
394
395    assert_equal 'text/html',         @res.content_type
396    assert_match %r%<title>Table of Contents - Standard Library Documentation%,
397                 @res.body
398  end
399
400  def test_show_documentation_page
401    touch_system_cache_path
402
403    @req.path = '/ruby/Missing.html'
404
405    @s.show_documentation @req, @res
406
407    assert_equal 404, @res.status
408  end
409
410  def test_show_documentation_search_index
411    touch_system_cache_path
412
413    @req.path = '/ruby/js/search_index.js'
414
415    @s.show_documentation @req, @res
416
417    assert_equal 'application/javascript', @res.content_type
418    assert_match %r%\Avar search_data =%,  @res.body
419  end
420
421  def test_store_for_gem
422    store = @s.store_for 'spec-1.0'
423
424    assert_equal File.join(@gem_doc_dir, 'spec-1.0', 'ri'), store.path
425    assert_equal :gem, store.type
426  end
427
428  def test_store_for_home
429    store = @s.store_for 'home'
430
431    assert_equal @home_dir, store.path
432    assert_equal :home, store.type
433  end
434
435  def test_store_for_missing
436    e = assert_raises RDoc::Error do
437      @s.store_for 'missing'
438    end
439
440    assert_equal 'could not find ri documentation for missing', e.message
441  end
442
443  def test_store_for_ruby
444    store = @s.store_for 'ruby'
445
446    assert_equal @system_dir, store.path
447    assert_equal :system, store.type
448  end
449
450  def test_store_for_site
451    store = @s.store_for 'site'
452
453    assert_equal File.join(@base, 'site'), store.path
454    assert_equal :site, store.type
455  end
456
457  def touch_system_cache_path
458    store = RDoc::Store.new @system_dir
459    store.title = 'Standard Library Documentation'
460
461    FileUtils.mkdir_p File.dirname store.cache_path
462
463    store.save
464  end
465
466end
467
468