rubytest 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/.ruby CHANGED
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  source:
3
3
  - var
4
+ - var/
4
5
  authors:
5
6
  - name: trans
6
7
  email: transfire@gmail.com
@@ -8,8 +9,6 @@ copyrights:
8
9
  - holder: RubyWorks
9
10
  year: '2011'
10
11
  license: BSD-2-Clause
11
- replacements: []
12
- alternatives: []
13
12
  requirements:
14
13
  - name: ansi
15
14
  - name: detroit
@@ -21,14 +20,15 @@ requirements:
21
20
  - test
22
21
  development: true
23
22
  dependencies: []
23
+ alternatives: []
24
24
  conflicts: []
25
25
  repositories:
26
26
  - uri: git@github.com:rubyworks/ruby-test.git
27
27
  scm: git
28
28
  name: upstream
29
29
  resources:
30
- home: http://rubyworks.github.com/ruby-test
31
- code: http://github.com/rubyworks/ruby-test
30
+ home: http://rubyworks.github.com/rubytest
31
+ code: http://github.com/rubyworks/rubytest
32
32
  mail: http://groups.google.com/group/rubyworks-mailinglist
33
33
  extra: {}
34
34
  load_path:
@@ -37,9 +37,9 @@ revision: 0
37
37
  created: '2011-07-23'
38
38
  summary: Ruby Universal Test Harness
39
39
  title: Ruby Test
40
- version: 0.3.1
40
+ version: 0.4.0
41
41
  name: rubytest
42
42
  description: ! "Ruby Test is a universal test harness for Ruby. It can handle any
43
43
  compliant \ntest framework, even running tests from multiple frameworks in a single
44
44
  pass."
45
- date: '2011-12-23'
45
+ date: '2012-02-25'
data/Gemfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rubytest (0.3.1)
5
+ ansi
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ ae (1.8.1)
11
+ ansi
12
+ ansi (1.4.1)
13
+ detroit (0.2.0)
14
+ facets
15
+ pom
16
+ facets (2.9.2)
17
+ pom (2.1.1)
18
+ qed (2.6.3)
19
+ ae (>= 1.7)
20
+ ansi
21
+ facets (>= 2.8)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ detroit
28
+ qed
29
+ rubytest!
data/HISTORY.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # RELEASE HISTORY
2
2
 
3
+ ## 0.4.0 / 2012-02-25
4
+
5
+ This release primarily consists of implementation tweaks. The most significant
6
+ change is in support of exception priorities, which justify the major verison bump.
7
+
8
+ Changes:
9
+
10
+ * Add preliminary support for exception priorities.
11
+ * Capture output for TAP-Y/J reporters.
12
+
13
+
3
14
  ## 0.3.0 / 2011-12-22
4
15
 
5
16
  Technically this is a fairly minor release that improves backtrace output
data/README.md CHANGED
@@ -30,12 +30,12 @@ to be a failed assertion rather than simply an error. Ruby Test extends the
30
30
  Exception class to support this method for all exceptions.
31
31
 
32
32
  A test framework may raise a `NotImplementedError` to have a test recorded
33
- as "pending" --a _todo_ item to remind the developer of tests that still
33
+ as *todo* --a _pending_ exception to remind the developer of tests that still
34
34
  need to be written. The `NotImplementedError` is a standard Ruby exception
35
- and a subclass of `ScriptError`.
36
-
37
- If the `NotImplmentedError` responds in the affirmative to `#assertion?` then
38
- the test is taken to be a purposeful _omission_, rather than simply pending.
35
+ and a subclass of `ScriptError`. The exception can also set a priority level
36
+ to indicate the urgency of the pending test. Priorities of -1 or lower
37
+ will generally not be brought to the attention of testers unless explicitly
38
+ configured to do so.
39
39
 
40
40
  That is the crux of Ruby Test specification. Ruby Test supports some
41
41
  additional features that can makes its usage even more convenient.
data/bin/ruby-test ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubytest'
3
+ Test::Runner.cli(*ARGV)
4
+
@@ -0,0 +1 @@
1
+ require 'ae'
@@ -0,0 +1,2 @@
1
+ require 'rubytest'
2
+
@@ -25,7 +25,7 @@ module Test
25
25
  #
26
26
  def initialize(file, line)
27
27
  @file = file
28
- @line = line || 1
28
+ @line = (line || 1).to_i
29
29
  @code = CodeSnippet.cache(file)
30
30
  end
31
31
 
@@ -1,8 +1,19 @@
1
1
  class Exception
2
+
2
3
  def set_assertion(boolean)
3
4
  @assertion = boolean
4
- end
5
+ end unless defined?(:set_assertion)
6
+
5
7
  def assertion?
6
8
  @assertion
7
- end
9
+ end unless defined?(:assertion?)
10
+
11
+ def set_priority(integer)
12
+ @priority = integer.to_i
13
+ end unless defined?(:set_priority)
14
+
15
+ def priority
16
+ @priority ||= 0
17
+ end unless defined?(:priority)
18
+
8
19
  end
@@ -0,0 +1,8 @@
1
+ class File
2
+
3
+ #
4
+ def self.localname(path)
5
+ path.sub(Dir.pwd+'/','')
6
+ end
7
+
8
+ end
@@ -1,9 +1,11 @@
1
1
  if RUBY_VERSION < '1.9'
2
2
  require 'rubytest/core_ext/assertion'
3
3
  require 'rubytest/core_ext/exception'
4
+ require 'rubytest/core_ext/file'
4
5
  require 'rubytest/core_ext/string'
5
6
  else
6
7
  require_relative 'core_ext/assertion'
7
8
  require_relative 'core_ext/exception'
9
+ require_relative 'core_ext/file'
8
10
  require_relative 'core_ext/string'
9
11
  end
@@ -187,7 +187,7 @@ module Test
187
187
  def clean_backtrace(exception)
188
188
  trace = (Exception === exception ? exception.backtrace : exception)
189
189
  return trace if $DEBUG
190
- trace = trace.reject{ |t| RUBY_IGNORE_CALLERS.any?{ |r| r =~ t }}
190
+ trace = trace.reject{ |t| $RUBY_IGNORE_CALLERS.any?{ |r| r =~ t }}
191
191
  trace = trace.map do |t|
192
192
  i = t.index(':in')
193
193
  i ? t[0...i] : t
@@ -198,7 +198,7 @@ module Test
198
198
  # exception.set_backtrace(trace) if Exception === exception
199
199
  # exception
200
200
  #end
201
- trace.uniq
201
+ trace.uniq.map{ |bt| File.localname(bt) }
202
202
  end
203
203
 
204
204
  # That an exception, backtrace or source code text and line
@@ -216,17 +216,13 @@ module Test
216
216
  end
217
217
  end
218
218
 
219
- #--
220
- # TODO: Show more of the file name than just the basename.
221
- #++
222
-
223
219
  #
224
220
  def file_and_line(exception)
225
221
  line = clean_backtrace(exception)[0]
226
222
  return "" unless line
227
223
  i = line.rindex(':in')
228
224
  line = i ? line[0...i] : line
229
- File.basename(line)
225
+ File.localname(line)
230
226
  end
231
227
 
232
228
  #
@@ -240,7 +236,7 @@ module Test
240
236
  return ["", 0] unless line
241
237
  i = line.rindex(':in')
242
238
  line = i ? line[0...i] : line
243
- f, l = File.basename(line).split(':')
239
+ f, l = File.localname(line).split(':')
244
240
  return [f, l.to_i]
245
241
  end
246
242
 
@@ -7,9 +7,12 @@ module Test::Reporters
7
7
  #
8
8
  class AbstractHash < Abstract
9
9
 
10
+ #
11
+ # @return [Hash]
10
12
  #
11
13
  def begin_suite(suite)
12
14
  require 'yaml'
15
+ require 'stringio'
13
16
 
14
17
  @start_time = Time.now
15
18
  @case_level = 0
@@ -23,9 +26,13 @@ module Test::Reporters
23
26
  'count' => total_count(suite)
24
27
  }
25
28
 
29
+ h['seed'] = suite.seed if suite.respond_to?(:seed)
30
+
26
31
  return h
27
32
  end
28
33
 
34
+ #
35
+ # @return [Hash]
29
36
  #
30
37
  def begin_case(test_case)
31
38
  h = {}
@@ -44,13 +51,21 @@ module Test::Reporters
44
51
  #
45
52
  def begin_test(test)
46
53
  @test_index += 1
54
+
55
+ @stdout, @stderr = $stdout, $stderr
56
+ $stdout, $stderr = StringIO.new, StringIO.new
47
57
  end
48
58
 
59
+ # Ruby Test use the term "skip", where as TAP-Y/J uses "omit".
49
60
  #
50
- def pass(test) #, backtrace=nil)
61
+ # @todo Maybe the terms can ultimately be reconciled.
62
+ #
63
+ # @return [Hash]
64
+ #
65
+ def skip_test(test)
51
66
  h = {}
52
67
  h['type' ] = 'test'
53
- h['status'] = 'pass'
68
+ h['status'] = 'omit'
54
69
 
55
70
  merge_subtype h, test
56
71
  merge_setup h, test
@@ -58,16 +73,20 @@ module Test::Reporters
58
73
  #merge_comparison h, test, exception
59
74
  #merge_coverage h, test
60
75
  merge_source h, test
76
+ #merge_exception h, test, exception
77
+ merge_output h
61
78
  merge_time h
62
79
 
63
80
  return h
64
81
  end
65
82
 
66
83
  #
67
- def fail(test, exception)
84
+ # @return [Hash]
85
+ #
86
+ def pass(test) #, backtrace=nil)
68
87
  h = {}
69
88
  h['type' ] = 'test'
70
- h['status'] = 'fail'
89
+ h['status'] = 'pass'
71
90
 
72
91
  merge_subtype h, test
73
92
  merge_setup h, test
@@ -75,75 +94,95 @@ module Test::Reporters
75
94
  #merge_comparison h, test, exception
76
95
  #merge_coverage h, test
77
96
  merge_source h, test
78
- merge_exception h, test, exception
97
+ merge_output h
79
98
  merge_time h
80
-
99
+
81
100
  return h
82
101
  end
83
102
 
84
103
  #
85
- def error(test, exception)
104
+ # @return [Hash]
105
+ #
106
+ def fail(test, exception)
86
107
  h = {}
87
108
  h['type' ] = 'test'
88
- h['status'] = 'error'
109
+ h['status'] = 'fail'
89
110
 
90
111
  merge_subtype h, test
112
+ merge_priority h, test, exception
91
113
  merge_setup h, test
92
114
  merge_label h, test
93
115
  #merge_comparison h, test, exception
94
116
  #merge_coverage h, test
95
117
  merge_source h, test
96
- merge_exception h, test, exception, true
118
+ merge_exception h, test, exception
119
+ merge_output h
97
120
  merge_time h
98
-
121
+
99
122
  return h
100
123
  end
101
124
 
102
125
  #
103
- def todo(test, exception)
126
+ # @return [Hash]
127
+ #
128
+ def error(test, exception)
104
129
  h = {}
105
130
  h['type' ] = 'test'
106
- h['status'] = 'todo'
131
+ h['status'] = 'error'
107
132
 
108
133
  merge_subtype h, test
134
+ merge_priority h, test, exception
109
135
  merge_setup h, test
110
136
  merge_label h, test
111
137
  #merge_comparison h, test, exception
112
138
  #merge_coverage h, test
113
139
  merge_source h, test
114
- merge_exception h, test, exception
140
+ merge_exception h, test, exception, true
141
+ merge_output h
115
142
  merge_time h
116
143
 
117
144
  return h
118
145
  end
119
146
 
120
147
  #
121
- def omit(test, exception)
148
+ # @return [Hash]
149
+ #
150
+ def todo(test, exception)
122
151
  h = {}
123
- h['type' ] = 'test'
124
- h['status'] = 'omit'
152
+ h['type' ] = 'test'
153
+ h['status'] = 'todo'
125
154
 
126
155
  merge_subtype h, test
156
+ merge_priority h, test, exception
127
157
  merge_setup h, test
128
158
  merge_label h, test
129
159
  #merge_comparison h, test, exception
130
160
  #merge_coverage h, test
131
161
  merge_source h, test
132
162
  merge_exception h, test, exception
163
+ merge_output h
133
164
  merge_time h
134
165
 
135
166
  return h
136
167
  end
137
168
 
169
+ #
170
+ def end_test(test)
171
+ super(test)
172
+ $stdout, $stderr = @stdout, @stderr
173
+ end
174
+
138
175
  #
139
176
  def end_case(test_case)
140
177
  @case_level -= 1
141
178
  end
142
179
 
180
+ #
181
+ # @return [Hash]
143
182
  #
144
183
  def end_suite(suite)
145
184
  h = {
146
- 'type' => 'tally',
185
+ 'type' => 'final',
147
186
  'time' => Time.now - @start_time,
148
187
  'counts' => {
149
188
  'total' => total,
@@ -159,12 +198,23 @@ module Test::Reporters
159
198
 
160
199
  private
161
200
 
201
+ # For todo entries in particulr, i.e. NotImplementedError
202
+ # exception, the return value represents the "todo" priority
203
+ # level. The `Exception#priority` method returns an Integer
204
+ # to set the priority level higher or lower, where higher
205
+ # the number the more urgent the priority.
206
+ #
207
+ def merge_priority(hash, test, exception)
208
+ level = exception.priority
209
+ h['priority'] = level.to_i
210
+ end
211
+
162
212
  #
163
213
  def merge_subtype(hash, test)
164
214
  hash['subtype'] = test.type.to_s if test.respond_to?(:type)
165
215
  end
166
216
 
167
- # TODO: topic or setup ?
217
+ # RubyTest uses the term `topic`, where as TAP-Y/J uses `setup`.
168
218
  def merge_setup(hash, test)
169
219
  #hash['setup'] = test.setup.to_s if test.respond_to?(:setup)
170
220
  hash['setup'] = test.topic.to_s if test.respond_to?(:topic)
@@ -175,7 +225,7 @@ module Test::Reporters
175
225
  hash['label'] = test.to_s.strip
176
226
  end
177
227
 
178
- # NOTE: Not presently used.
228
+ # TODO: This is not presently used.
179
229
  def merge_comparison(hash, test, exception)
180
230
  hash['returned'] = exception.returned
181
231
  hash['expected'] = exception.expected
@@ -203,17 +253,37 @@ module Test::Reporters
203
253
  hash['exception']['backtrace'] = clean_backtrace(exception) if bt
204
254
  end
205
255
 
206
- # TODO: Really?
256
+ # TODO: This is still an "idea in progress" for both RybyTest and Tap-Y/J.
257
+ #
258
+ # There are a number of different types of code coverage.
259
+ #
260
+ # http://en.wikipedia.org/wiki/Code_coverage
261
+ #
262
+ # If we were to provide this, we'd have LOC coverage, probably
263
+ # given as a list of `file:from-to`, and UNIT coverage, a list
264
+ # of classes/modules and methods addressed.
265
+ #
207
266
  def merge_coverage(hash, test)
208
- if test.respond_to?(:file_coverage) or test.respond_to?(:code_coverage)
209
- fc = test.file_coverage
210
- cc = test.code_coverage
267
+ loc, unit = nil, nil
268
+ if test.respond_to?(:loc)
269
+ loc = test.loc
270
+ end
271
+ if test.respond_to?(:unit)
272
+ unit = test.unit
273
+ end
274
+ if loc or unit
211
275
  hash['coverage'] = {}
212
- hash['coverage']['file'] = fc if fc
213
- hash['coverage']['code'] = cc if cc
276
+ hash['coverage']['loc'] = loc if loc
277
+ hash['coverage']['unit'] = unit if unit
214
278
  end
215
279
  end
216
280
 
281
+ #
282
+ def merge_output(hash)
283
+ hash['stdout'] = $stdout.string
284
+ hash['stderr'] = $stderr.string
285
+ end
286
+
217
287
  #
218
288
  def merge_time(hash)
219
289
  hash['time'] = Time.now - @start_time
@@ -66,6 +66,7 @@ module Test::Reporters
66
66
  puts " #{exception}"
67
67
  puts " #{file_and_line(exception)}"
68
68
  puts code(exception)
69
+ puts " " + clean_backtrace(exception).join("\n ")
69
70
  puts
70
71
  end
71
72
  end
@@ -77,6 +78,7 @@ module Test::Reporters
77
78
  puts " #{exception}"
78
79
  puts " #{file_and_line(exception)}"
79
80
  puts code(exception)
81
+ puts " " + clean_backtrace(exception).join("\n ")
80
82
  puts
81
83
  end
82
84
  end
@@ -6,6 +6,8 @@ module Test::Reporters
6
6
  #
7
7
  class Tapj < AbstractHash
8
8
 
9
+ REVISION = 5
10
+
9
11
  #
10
12
  def initialize(runner)
11
13
  require 'json'
@@ -14,7 +16,9 @@ module Test::Reporters
14
16
 
15
17
  #
16
18
  def begin_suite(suite)
17
- puts super(suite).to_json
19
+ hash = super(suite)
20
+ hash['rev'] = REVISION
21
+ puts hash.to_json
18
22
  end
19
23
 
20
24
  #
@@ -6,6 +6,8 @@ module Test::Reporters
6
6
  #
7
7
  class Tapy < AbstractHash
8
8
 
9
+ REVISION = 5
10
+
9
11
  #
10
12
  def initialize(runner)
11
13
  require 'json'
@@ -14,7 +16,9 @@ module Test::Reporters
14
16
 
15
17
  #
16
18
  def begin_suite(suite)
17
- puts super(suite).to_yaml
19
+ hash = super(suite)
20
+ hash['rev'] = REVISION
21
+ puts hash.to_json
18
22
  end
19
23
 
20
24
  #
@@ -168,15 +168,16 @@ module Test
168
168
 
169
169
  private
170
170
 
171
- # Add to RUBY_IGNORE_CALLERS.
171
+ # Add to $RUBY_IGNORE_CALLERS.
172
172
  #
173
173
  # @todo Improve on this!
174
174
  def ignore_callers
175
175
  ignore_path = File.expand_path(File.join(__FILE__, '../../..'))
176
176
  ignore_regexp = Regexp.new(Regexp.escape(ignore_path))
177
177
 
178
- ::RUBY_IGNORE_CALLERS << ignore_regexp
179
- ::RUBY_IGNORE_CALLERS << /bin\/rubytest/
178
+ $RUBY_IGNORE_CALLERS ||= {}
179
+ $RUBY_IGNORE_CALLERS << ignore_regexp
180
+ $RUBY_IGNORE_CALLERS << /bin\/rubytest/
180
181
  end
181
182
 
182
183
  #
@@ -233,11 +234,11 @@ module Test
233
234
  rescue *OPEN_ERRORS => exception
234
235
  raise exception
235
236
  rescue NotImplementedError => exception
236
- if exception.assertion?
237
- observers.each{ |o| o.omit(test, exception) }
238
- else
237
+ #if exception.assertion? # TODO: May require assertion? for todo in future
239
238
  observers.each{ |o| o.todo(test, exception) }
240
- end
239
+ #else
240
+ # observers.each{ |o| o.error(test, exception) }
241
+ #end
241
242
  rescue Exception => exception
242
243
  if exception.assertion?
243
244
  observers.each{ |o| o.fail(test, exception) }
data/lib/rubytest.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # Ruby Test - a Universal Ruby Test Harness
2
2
 
3
3
  $TEST_SUITE = [] unless defined?($TEST_SUITE)
4
+ $RUBY_IGNORE_CALLERS = [] unless defined?($RUBY_IGNORE_CALLERS)
4
5
 
5
- RUBY_IGNORE_CALLERS = [] unless defined?(RUBY_IGNORE_CALLERS)
6
-
6
+ require 'brass'
7
7
  require 'ansi/core'
8
8
 
9
9
  if RUBY_VERSION < '1.9'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubytest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-23 00:00:00.000000000 Z
12
+ date: 2012-02-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ansi
16
- requirement: &31513800 !ruby/object:Gem::Requirement
16
+ requirement: &25844320 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *31513800
24
+ version_requirements: *25844320
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: detroit
27
- requirement: &31513160 !ruby/object:Gem::Requirement
27
+ requirement: &25843760 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *31513160
35
+ version_requirements: *25843760
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: qed
38
- requirement: &31512480 !ruby/object:Gem::Requirement
38
+ requirement: &25843200 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,13 +43,14 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *31512480
46
+ version_requirements: *25843200
47
47
  description: ! "Ruby Test is a universal test harness for Ruby. It can handle any
48
48
  compliant \ntest framework, even running tests from multiple frameworks in a single
49
49
  pass."
50
50
  email:
51
51
  - transfire@gmail.com
52
52
  executables:
53
+ - ruby-test
53
54
  - rubytest
54
55
  extensions: []
55
56
  extra_rdoc_files:
@@ -59,16 +60,19 @@ extra_rdoc_files:
59
60
  files:
60
61
  - .ruby
61
62
  - .test
63
+ - bin/ruby-test
62
64
  - bin/rubytest
63
65
  - demo/01_test.md
64
66
  - demo/02_case.md
65
- - demo/applique/ruby-test.rb
67
+ - demo/applique/ae.rb
68
+ - demo/applique/rubytest.rb
66
69
  - lib/rubytest/autorun.rb
67
70
  - lib/rubytest/cli.rb
68
71
  - lib/rubytest/code_snippet.rb
69
72
  - lib/rubytest/config.rb
70
73
  - lib/rubytest/core_ext/assertion.rb
71
74
  - lib/rubytest/core_ext/exception.rb
75
+ - lib/rubytest/core_ext/file.rb
72
76
  - lib/rubytest/core_ext/string.rb
73
77
  - lib/rubytest/core_ext.rb
74
78
  - lib/rubytest/rake.rb
@@ -88,10 +92,11 @@ files:
88
92
  - lib/rubytest.rb
89
93
  - lib/test.rb
90
94
  - test/basic_case.rb
95
+ - Gemfile.lock
91
96
  - HISTORY.md
92
97
  - README.md
93
98
  - NOTICE.md
94
- homepage: http://rubyworks.github.com/ruby-test
99
+ homepage: http://rubyworks.github.com/rubytest
95
100
  licenses:
96
101
  - BSD-2-Clause
97
102
  post_install_message:
@@ -112,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
117
  version: '0'
113
118
  requirements: []
114
119
  rubyforge_project:
115
- rubygems_version: 1.8.10
120
+ rubygems_version: 1.8.11
116
121
  signing_key:
117
122
  specification_version: 3
118
123
  summary: Ruby Universal Test Harness
@@ -1,2 +0,0 @@
1
- require 'test/runner'
2
-