repobrowse 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitattributes +5 -0
- data/.gitignore +3 -0
- data/COPYING +661 -0
- data/GNUmakefile +38 -0
- data/MANIFEST +42 -0
- data/README +35 -0
- data/lib/repobrowse.rb +7 -0
- data/lib/repobrowse/app.rb +60 -0
- data/lib/repobrowse/config.rb +66 -0
- data/lib/repobrowse/error.rb +21 -0
- data/lib/repobrowse/escape.rb +28 -0
- data/lib/repobrowse/git.rb +71 -0
- data/lib/repobrowse/git_atom.rb +109 -0
- data/lib/repobrowse/git_commit_html.rb +320 -0
- data/lib/repobrowse/git_disambiguate.rb +21 -0
- data/lib/repobrowse/git_http_backend.rb +109 -0
- data/lib/repobrowse/git_log.rb +4 -0
- data/lib/repobrowse/git_patch.rb +55 -0
- data/lib/repobrowse/git_raw.rb +50 -0
- data/lib/repobrowse/git_raw_tree_html.rb +50 -0
- data/lib/repobrowse/git_show.rb +32 -0
- data/lib/repobrowse/git_src.rb +37 -0
- data/lib/repobrowse/git_src_blob_html.rb +89 -0
- data/lib/repobrowse/git_src_tree_html.rb +118 -0
- data/lib/repobrowse/html.rb +66 -0
- data/lib/repobrowse/limit_rd.rb +86 -0
- data/lib/repobrowse/pipe_body.rb +25 -0
- data/lib/repobrowse/repo.rb +21 -0
- data/lib/repobrowse/static.rb +96 -0
- data/repobrowse.gemspec +29 -0
- data/test/covshow.rb +30 -0
- data/test/git.fast-import-data +101 -0
- data/test/helper.rb +182 -0
- data/test/test_config.rb +29 -0
- data/test/test_git.rb +15 -0
- data/test/test_git_atom.rb +27 -0
- data/test/test_git_clone.rb +73 -0
- data/test/test_git_patch.rb +44 -0
- data/test/test_git_raw_tree_html.rb +34 -0
- data/test/test_git_show.rb +17 -0
- data/test/test_html.rb +23 -0
- metadata +139 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
2
|
+
# Copyright (C) 2017-2018 all contributors <repobrowse-public@80x24.org>
|
|
3
|
+
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
|
|
4
|
+
# frozen_string_literal: false
|
|
5
|
+
|
|
6
|
+
class Repobrowse::LimitRd
|
|
7
|
+
def initialize(rd, size, buf, &blk)
|
|
8
|
+
@rd = rd # git cat-file --batch output
|
|
9
|
+
@left = size == 0 ? nil : size
|
|
10
|
+
@buf = buf
|
|
11
|
+
@on_close = blk
|
|
12
|
+
@peek = nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# called by Rack server in ensure
|
|
16
|
+
def close
|
|
17
|
+
@on_close&.call(self) # allows our @rd to be reused
|
|
18
|
+
@buf&.clear
|
|
19
|
+
@peek&.clear
|
|
20
|
+
@on_close = nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# used to determine if a file is binary or text
|
|
24
|
+
def peek(len = 8000) # 8000 bytes is the same size used by git
|
|
25
|
+
@peek = read(len, @buf)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# called by Rack server
|
|
29
|
+
def each
|
|
30
|
+
peek, @peek = @peek, nil
|
|
31
|
+
yield peek if peek
|
|
32
|
+
while read(16384, @buf)
|
|
33
|
+
yield @buf
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# non-Rack response body interface
|
|
38
|
+
def read(len = nil, buf = nil)
|
|
39
|
+
raise RuntimeError, 'not #read is compatible with #peek' if @peek
|
|
40
|
+
if @left
|
|
41
|
+
len = @left if len.nil? || len > @left
|
|
42
|
+
ret = @rd.read(len, buf)
|
|
43
|
+
@left -= ret.bytesize if ret
|
|
44
|
+
@left = nil if @left == 0
|
|
45
|
+
ret
|
|
46
|
+
else
|
|
47
|
+
buf&.clear
|
|
48
|
+
len ? nil : ''
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# non-Rack response body interface
|
|
53
|
+
def gets(sep = $/, limit = nil, chomp: false)
|
|
54
|
+
if Integer === sep && limit.nil?
|
|
55
|
+
limit = sep
|
|
56
|
+
sep = $/
|
|
57
|
+
end
|
|
58
|
+
raise RuntimeError, 'not #read is compatible with #peek' if @peek
|
|
59
|
+
return if @left.nil?
|
|
60
|
+
return '' if limit == 0
|
|
61
|
+
limit = @left if limit.nil? || limit > @left
|
|
62
|
+
if limit == 0
|
|
63
|
+
@left = nil
|
|
64
|
+
return nil
|
|
65
|
+
end
|
|
66
|
+
buf = @rd.gets(sep, limit)
|
|
67
|
+
if buf
|
|
68
|
+
@left -= buf.bytesize
|
|
69
|
+
@left = nil if @left == 0
|
|
70
|
+
|
|
71
|
+
# we must chomp ourselves for accounting @left
|
|
72
|
+
buf.chomp!(sep) if chomp
|
|
73
|
+
end
|
|
74
|
+
buf
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# we must drain the buffer if the reader aborted prematurely
|
|
78
|
+
def drain
|
|
79
|
+
n = @left or return
|
|
80
|
+
max = 16384
|
|
81
|
+
while n > 0
|
|
82
|
+
len = n > max ? max : n
|
|
83
|
+
@rd.read(len, @buf) and n -= @buf.bytesize
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
2
|
+
# Copyright (C) 2017-2018 all contributors <repobrowse-public@80x24.org>
|
|
3
|
+
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
class Repobrowse::PipeBody
|
|
6
|
+
attr_reader :to_io
|
|
7
|
+
|
|
8
|
+
def initialize(io, buf)
|
|
9
|
+
@to_io = io
|
|
10
|
+
@buf = buf
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# called by Rack server
|
|
14
|
+
def each
|
|
15
|
+
begin
|
|
16
|
+
yield @buf
|
|
17
|
+
end while @to_io.read(0x4000, @buf)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# called by Rack server
|
|
21
|
+
def close
|
|
22
|
+
@buf.clear
|
|
23
|
+
@to_io.close
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright (C) 2017-2018 all contributors <repobrowse-public@80x24.org>
|
|
2
|
+
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
#
|
|
5
|
+
# class for represpenting an inbox git repository.
|
|
6
|
+
class Repobrowse::Repo
|
|
7
|
+
attr_reader :driver
|
|
8
|
+
attr_reader :name
|
|
9
|
+
attr_reader :path
|
|
10
|
+
attr_reader :lineno_prefix
|
|
11
|
+
|
|
12
|
+
def initialize(opts)
|
|
13
|
+
@lineno_prefix = 'n' # 'l' for gitweb conversions
|
|
14
|
+
opts.each { |k, v| instance_variable_set "@#{k}", v }
|
|
15
|
+
case @vcs
|
|
16
|
+
when 'git'
|
|
17
|
+
@driver = Repobrowse::Git.new(@path)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Copyright (C) 2017-2018 all contributors <repobrowse-public@80x24.org>
|
|
2
|
+
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
require_relative 'limit_rd'
|
|
5
|
+
|
|
6
|
+
module Repobrowse::Static
|
|
7
|
+
|
|
8
|
+
# Rack response body
|
|
9
|
+
class F < File
|
|
10
|
+
def each
|
|
11
|
+
while buf = read(8192, buf)
|
|
12
|
+
yield buf
|
|
13
|
+
end
|
|
14
|
+
ensure
|
|
15
|
+
buf&.clear
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def fopen(r, pathname)
|
|
20
|
+
F.open(pathname)
|
|
21
|
+
rescue SystemCallError
|
|
22
|
+
r404(r)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def prepare_range(env, r, fp, h, beg_s, fin_s, size)
|
|
26
|
+
code = 200
|
|
27
|
+
len = size
|
|
28
|
+
beg = beg_s.to_i
|
|
29
|
+
fin = fin_s.to_i
|
|
30
|
+
if beg_s == ''
|
|
31
|
+
if fin_s != '' # "bytes=-end" => last N bytes
|
|
32
|
+
beg = size - fin
|
|
33
|
+
beg = 0 if beg < 0
|
|
34
|
+
fin = size - 1
|
|
35
|
+
code = 206
|
|
36
|
+
else
|
|
37
|
+
code = 416
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
if beg > size
|
|
41
|
+
code = 416
|
|
42
|
+
elsif fin_s == '' || fin >= size
|
|
43
|
+
fin = size - 1
|
|
44
|
+
code = 206
|
|
45
|
+
elsif fin < size
|
|
46
|
+
code = 206
|
|
47
|
+
else
|
|
48
|
+
code = 416
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
if code == 206
|
|
52
|
+
len = fin - beg + 1;
|
|
53
|
+
if len <= 0
|
|
54
|
+
code = 416
|
|
55
|
+
else
|
|
56
|
+
# most Rack servers do not handle range requests correctly
|
|
57
|
+
# if they use .to_path
|
|
58
|
+
fp.autoclose = false
|
|
59
|
+
fp.seek(beg, IO::SEEK_SET) or r.halt [ 500, [], [] ]
|
|
60
|
+
fp = Repobrowse::LimitRd.new(fp, fin - beg + 1, ''.b)
|
|
61
|
+
h['Accept-Ranges'] = -'bytes'
|
|
62
|
+
h['Content-Range'] = "bytes #{beg}-#{fin}/#{size}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
if code == 416
|
|
66
|
+
h['Content-Range'] = -"bytes */#{size}"
|
|
67
|
+
r.halt [ 416, h, [] ]
|
|
68
|
+
end
|
|
69
|
+
[ code, len, fp ]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# expires in 1 year
|
|
73
|
+
def static(r, pathname, type, exp = 31536000)
|
|
74
|
+
r.get do
|
|
75
|
+
fp = fopen(r, pathname)
|
|
76
|
+
h = { 'Content-Type' => type }
|
|
77
|
+
if exp
|
|
78
|
+
h['Expires'] = -((Time.now + exp).httpdate)
|
|
79
|
+
h['Cache-Control'] = -"public, max-age=#{exp}"
|
|
80
|
+
else
|
|
81
|
+
h['Expires'] = 'Fri, 01 Jan 1980 00:00:00 GMT'
|
|
82
|
+
h['Pragma'] = 'no-cache'
|
|
83
|
+
h['Cache-Control'] = 'no-cache, max-age=0, must-revalidate'
|
|
84
|
+
end
|
|
85
|
+
# TODO: If-Modified-Since and Last-Modified?
|
|
86
|
+
code = 200
|
|
87
|
+
st = fp.stat
|
|
88
|
+
size = st.size
|
|
89
|
+
if env['HTTP_RANGE'] =~ /\bbytes=(\d*)-(\d*)\z/
|
|
90
|
+
code, size, fp = prepare_range(env, r, fp, h, $1, $2, size)
|
|
91
|
+
end
|
|
92
|
+
h['Content-Length'] = -size.to_s
|
|
93
|
+
r.halt [ code, h, fp ]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/repobrowse.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
git_manifest = `git ls-files 2>/dev/null`.split("\n")
|
|
2
|
+
manifest = File.exist?('MANIFEST') ?
|
|
3
|
+
File.readlines('MANIFEST').map!(&:chomp).delete_if(&:empty?) : git_manifest
|
|
4
|
+
if git_manifest[0] && manifest != git_manifest
|
|
5
|
+
tmp = "MANIFEST.#$$.tmp"
|
|
6
|
+
File.open(tmp, 'w') { |fp| fp.puts(git_manifest.join("\n")) }
|
|
7
|
+
File.rename(tmp, 'MANIFEST')
|
|
8
|
+
system('git add MANIFEST')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Gem::Specification.new do |s|
|
|
12
|
+
s.name = %q{repobrowse}
|
|
13
|
+
s.version = (ENV['VERSION'] || '0.0.0').dup
|
|
14
|
+
s.homepage = 'https://80x24.org/repobrowse/'
|
|
15
|
+
s.authors = ['repobrowse hackers']
|
|
16
|
+
s.description = File.read('README').split("\n\n")[1]
|
|
17
|
+
s.email = %q{repobrowse-public@80x24.org}
|
|
18
|
+
s.files = manifest
|
|
19
|
+
s.summary = File.readlines('README')[0]
|
|
20
|
+
s.test_files = Dir['test/test_*.rb']
|
|
21
|
+
s.add_development_dependency('test-unit', '~> 3.0')
|
|
22
|
+
|
|
23
|
+
# hope this gets into
|
|
24
|
+
s.add_dependency('roda', '~> 3.3')
|
|
25
|
+
# TODO: make rugged optional when we start supporting other VCS
|
|
26
|
+
s.add_dependency('rugged', '~> 0.24') # whatever's in Debian stretch
|
|
27
|
+
s.required_ruby_version = '>= 2.3'
|
|
28
|
+
s.licenses = %w(AGPL-3.0+)
|
|
29
|
+
end
|
data/test/covshow.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright (C) 2017-2018 all contributors <repobrowse-public@80x24.org>
|
|
2
|
+
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
#
|
|
5
|
+
# this works with the __covmerge method in test/helper.rb
|
|
6
|
+
# run this file after all tests are run
|
|
7
|
+
|
|
8
|
+
# load the merged dump data
|
|
9
|
+
res = Marshal.load(IO.binread("coverage.dump"))
|
|
10
|
+
|
|
11
|
+
# Dirty little text formatter. I tried simplecov but the default
|
|
12
|
+
# HTML+JS is unusable without a GUI (I hate GUIs :P) and it would've
|
|
13
|
+
# taken me longer to search the Internets to find a plain-text
|
|
14
|
+
# formatter I like...
|
|
15
|
+
res.keys.sort.each do |filename|
|
|
16
|
+
cov = res[filename]
|
|
17
|
+
puts "==> #{filename} <=="
|
|
18
|
+
File.readlines(filename).each_with_index do |line, i|
|
|
19
|
+
n = cov[i]
|
|
20
|
+
if n == 0 # BAD
|
|
21
|
+
print(" *** 0 #{line}")
|
|
22
|
+
elsif n
|
|
23
|
+
printf("% 7u %s", n, line)
|
|
24
|
+
elsif line =~ /\S/ # probably a line with just "end" in it
|
|
25
|
+
print(" #{line}")
|
|
26
|
+
else # blank line
|
|
27
|
+
print "\n" # don't output trailing whitespace on blank lines
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
blob
|
|
2
|
+
mark :1
|
|
3
|
+
data 6
|
|
4
|
+
hello
|
|
5
|
+
|
|
6
|
+
reset refs/heads/header
|
|
7
|
+
commit refs/heads/header
|
|
8
|
+
mark :2
|
|
9
|
+
author AU Thor <e@example.com> 0 +0000
|
|
10
|
+
committer AU Thor <e@example.com> 0 +0000
|
|
11
|
+
data 8
|
|
12
|
+
initial
|
|
13
|
+
M 100644 :1 foo.txt
|
|
14
|
+
|
|
15
|
+
blob
|
|
16
|
+
mark :3
|
|
17
|
+
data 12
|
|
18
|
+
hello
|
|
19
|
+
world
|
|
20
|
+
|
|
21
|
+
commit refs/heads/master
|
|
22
|
+
mark :4
|
|
23
|
+
author AU Thor <e@example.com> 0 +0000
|
|
24
|
+
committer AU Thor <e@example.com> 0 +0000
|
|
25
|
+
data 7
|
|
26
|
+
second
|
|
27
|
+
from :2
|
|
28
|
+
M 100644 :3 foo.txt
|
|
29
|
+
|
|
30
|
+
blob
|
|
31
|
+
mark :5
|
|
32
|
+
data 12
|
|
33
|
+
-----
|
|
34
|
+
hello
|
|
35
|
+
|
|
36
|
+
commit refs/heads/header
|
|
37
|
+
mark :6
|
|
38
|
+
author AU Thor <e@example.com> 0 +0000
|
|
39
|
+
committer AU Thor <e@example.com> 0 +0000
|
|
40
|
+
data 11
|
|
41
|
+
add header
|
|
42
|
+
from :2
|
|
43
|
+
M 100644 :5 foo.txt
|
|
44
|
+
|
|
45
|
+
blob
|
|
46
|
+
mark :7
|
|
47
|
+
data 18
|
|
48
|
+
-----
|
|
49
|
+
hello
|
|
50
|
+
world
|
|
51
|
+
|
|
52
|
+
commit refs/heads/master
|
|
53
|
+
mark :8
|
|
54
|
+
author AU Thor <e@example.com> 0 +0000
|
|
55
|
+
committer AU Thor <e@example.com> 0 +0000
|
|
56
|
+
data 46
|
|
57
|
+
Merge branch 'header'
|
|
58
|
+
|
|
59
|
+
* header:
|
|
60
|
+
add header
|
|
61
|
+
from :4
|
|
62
|
+
merge :6
|
|
63
|
+
M 100644 :7 foo.txt
|
|
64
|
+
|
|
65
|
+
blob
|
|
66
|
+
mark :9
|
|
67
|
+
data 0
|
|
68
|
+
|
|
69
|
+
blob
|
|
70
|
+
mark :10
|
|
71
|
+
data 16
|
|
72
|
+
dir/dur/der/derp
|
|
73
|
+
commit refs/heads/master
|
|
74
|
+
mark :11
|
|
75
|
+
author AU Thor <e@example.com> 0 +0000
|
|
76
|
+
committer AU Thor <e@example.com> 0 +0000
|
|
77
|
+
data 26
|
|
78
|
+
add symlink and deep file
|
|
79
|
+
from :8
|
|
80
|
+
M 100644 :9 dir/dur/der/derp
|
|
81
|
+
M 120000 :10 link
|
|
82
|
+
|
|
83
|
+
blob
|
|
84
|
+
mark :12
|
|
85
|
+
data 78
|
|
86
|
+
[submodule "git"]
|
|
87
|
+
path = git
|
|
88
|
+
url = git://git.kernel.org/pub/scm/git/git.git
|
|
89
|
+
|
|
90
|
+
commit refs/heads/master
|
|
91
|
+
mark :13
|
|
92
|
+
author AU Thor <e@example.com> 0 +0000
|
|
93
|
+
committer AU Thor <e@example.com> 0 +0000
|
|
94
|
+
data 18
|
|
95
|
+
add git submodule
|
|
96
|
+
from :11
|
|
97
|
+
M 100644 :12 .gitmodules
|
|
98
|
+
M 160000 f3adf457e046f92f039353762a78dcb3afb2cb13 git
|
|
99
|
+
|
|
100
|
+
reset refs/heads/master
|
|
101
|
+
from :13
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Copyright (C) 2017-2018 all contributors <repobrowse-public@80x24.org>
|
|
2
|
+
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
$-w = $stdout.sync = $stderr.sync = Thread.abort_on_exception = true
|
|
5
|
+
|
|
6
|
+
# fork-aware coverage data gatherer, see also test/covshow.rb
|
|
7
|
+
if ENV["COVERAGE"]
|
|
8
|
+
require "coverage"
|
|
9
|
+
COVMATCH = %r{\brepobrowse\b}
|
|
10
|
+
COVDUMPFILE = File.expand_path("coverage.dump")
|
|
11
|
+
|
|
12
|
+
def __covmerge
|
|
13
|
+
res = Coverage.result
|
|
14
|
+
|
|
15
|
+
# do not create the file, Makefile does this before any tests run
|
|
16
|
+
File.open(COVDUMPFILE, IO::RDWR) do |covtmp|
|
|
17
|
+
covtmp.binmode
|
|
18
|
+
covtmp.sync = true
|
|
19
|
+
|
|
20
|
+
# we own this file (at least until somebody tries to use NFS :x)
|
|
21
|
+
covtmp.flock(File::LOCK_EX)
|
|
22
|
+
|
|
23
|
+
prev = covtmp.read
|
|
24
|
+
prev = prev.empty? ? {} : Marshal.load(prev)
|
|
25
|
+
res.each do |filename, counts|
|
|
26
|
+
# filter out stuff that's not in our project
|
|
27
|
+
COVMATCH =~ filename or next
|
|
28
|
+
|
|
29
|
+
# For compatibility with https://bugs.ruby-lang.org/issues/9508
|
|
30
|
+
# TODO: support those features if that gets merged into mainline
|
|
31
|
+
unless Array === counts
|
|
32
|
+
counts = counts[:lines]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
merge = prev[filename] || []
|
|
36
|
+
merge = merge
|
|
37
|
+
counts.each_with_index do |count, i|
|
|
38
|
+
count or next
|
|
39
|
+
merge[i] = (merge[i] || 0) + count
|
|
40
|
+
end
|
|
41
|
+
prev[filename] = merge
|
|
42
|
+
end
|
|
43
|
+
covtmp.rewind
|
|
44
|
+
covtmp.truncate(0)
|
|
45
|
+
covtmp.write(Marshal.dump(prev))
|
|
46
|
+
covtmp.flock(File::LOCK_UN)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Coverage.start
|
|
51
|
+
require 'test/unit'
|
|
52
|
+
Test::Unit.at_exit { __covmerge }
|
|
53
|
+
else
|
|
54
|
+
require 'test/unit'
|
|
55
|
+
end
|
|
56
|
+
require 'thread'
|
|
57
|
+
require 'fileutils'
|
|
58
|
+
require 'tempfile'
|
|
59
|
+
require 'tmpdir'
|
|
60
|
+
require 'webrick'
|
|
61
|
+
require 'repobrowse'
|
|
62
|
+
|
|
63
|
+
def git_tmp_repo
|
|
64
|
+
Dir.mktmpdir do |tmp_dir|
|
|
65
|
+
git_dir = "#{tmp_dir}/tmp.git"
|
|
66
|
+
assert(system(*%W(git init -q --bare #{git_dir})), 'initialize git dir')
|
|
67
|
+
assert(system(*%W(git --git-dir=#{git_dir} fast-import --quiet),
|
|
68
|
+
in: "#{File.dirname(__FILE__)}/git.fast-import-data"),
|
|
69
|
+
'fast-import test repo')
|
|
70
|
+
projects = { 'repo.tmp.path' => git_dir }
|
|
71
|
+
@app = Rack::Builder.new { run Repobrowse::App.new(projects) }.to_app
|
|
72
|
+
@req = Rack::MockRequest.new(@app)
|
|
73
|
+
yield tmp_dir, git_dir
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def rack_server(app)
|
|
78
|
+
readyq = Queue.new
|
|
79
|
+
logq = Queue.new
|
|
80
|
+
host = '127.0.0.1'
|
|
81
|
+
config = {
|
|
82
|
+
Logger: WEBrick::Log.new(logq),
|
|
83
|
+
app: app,
|
|
84
|
+
Host: host,
|
|
85
|
+
Port: 0, # WEBrick will choose a random port
|
|
86
|
+
server: 'webrick',
|
|
87
|
+
}
|
|
88
|
+
config[:AccessLog] = [] unless test_verbose?
|
|
89
|
+
|
|
90
|
+
th = Thread.new do
|
|
91
|
+
srv = Rack::Server.new(config)
|
|
92
|
+
srv.start { |s| readyq.push(s) }
|
|
93
|
+
end
|
|
94
|
+
s = readyq.pop
|
|
95
|
+
assert_instance_of WEBrick::HTTPServer, s
|
|
96
|
+
|
|
97
|
+
# grab the random port chosen by WEBrick
|
|
98
|
+
case l = logq.pop
|
|
99
|
+
when / port=(\d+)\n/
|
|
100
|
+
port = $1.to_i
|
|
101
|
+
break
|
|
102
|
+
end while true
|
|
103
|
+
|
|
104
|
+
Thread.new do
|
|
105
|
+
while l = logq.pop
|
|
106
|
+
warn "W: #{l.inspect}"
|
|
107
|
+
end
|
|
108
|
+
end if test_verbose?
|
|
109
|
+
|
|
110
|
+
yield logq, host, port
|
|
111
|
+
ensure
|
|
112
|
+
s&.shutdown
|
|
113
|
+
th&.join
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def test_verbose?
|
|
117
|
+
ENV['TEST_VERBOSE'].to_i != 0
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Rack::MockRequest doesn't always work since it converts the
|
|
121
|
+
# response into an array without dup-ing Strings, so response
|
|
122
|
+
# bodies which reuse a buffer (when they assume they're writing
|
|
123
|
+
# to a socket) fail
|
|
124
|
+
def req(method, uri, opts = {})
|
|
125
|
+
o = { method: method }.merge!(opts)
|
|
126
|
+
env = @req.class.env_for(uri, o)
|
|
127
|
+
@app.call(env)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def body_string(body)
|
|
131
|
+
rv = ''.b
|
|
132
|
+
body.each { |chunk| rv << chunk }
|
|
133
|
+
rv
|
|
134
|
+
ensure
|
|
135
|
+
body.close if body.respond_to?(:close)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
$tidy = nil
|
|
139
|
+
def tidy_check(input)
|
|
140
|
+
if $tidy.nil?
|
|
141
|
+
case v = `tidy --version 2>/dev/null`
|
|
142
|
+
when /version (\d+)\.\d+\.\d+/ # HTML Tidy for Linux/x86 version 5.2.0
|
|
143
|
+
major = $1.to_i
|
|
144
|
+
$tidy = major
|
|
145
|
+
when ''
|
|
146
|
+
$tidy = false
|
|
147
|
+
else # "HTML Tidy for Linux released on 25 March 2009"
|
|
148
|
+
$tidy = 4
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
if $tidy == false
|
|
152
|
+
begin
|
|
153
|
+
body.each { |buf| assert_instance_of(String, buf) }
|
|
154
|
+
ensure
|
|
155
|
+
body.close
|
|
156
|
+
end
|
|
157
|
+
else
|
|
158
|
+
rd, wr = IO.pipe
|
|
159
|
+
th = Thread.new(input) do |body|
|
|
160
|
+
begin
|
|
161
|
+
first = true
|
|
162
|
+
body.each do |chunk|
|
|
163
|
+
if first && $tidy < 5 # HTML5 allows optional type
|
|
164
|
+
first = false
|
|
165
|
+
chunk = chunk.sub(/<style>/, '<style type="application/css">')
|
|
166
|
+
end
|
|
167
|
+
wr.write(chunk)
|
|
168
|
+
end
|
|
169
|
+
ensure
|
|
170
|
+
wr.close
|
|
171
|
+
body.close
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
pid = spawn("tidy -q", in: rd, out: IO::NULL)
|
|
175
|
+
_, status = Process.waitpid2(pid)
|
|
176
|
+
assert_predicate status, :success?, status.inspect
|
|
177
|
+
end
|
|
178
|
+
ensure
|
|
179
|
+
th&.join
|
|
180
|
+
rd&.close
|
|
181
|
+
wr&.close
|
|
182
|
+
end
|