testml 0.0.1 → 0.0.2
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.
- data/.gemspec +3 -1
- data/CHANGELOG.yaml +4 -1
- data/LICENSE +1 -1
- data/README.rdoc +30 -5
- data/Rakefile +10 -1
- data/ToDo +56 -0
- data/lib/rake/testml.rb +14 -0
- data/lib/testml.rb +46 -2
- data/lib/testml/bridge.rb +5 -0
- data/lib/testml/compiler.rb +106 -0
- data/lib/testml/compiler/lite.rb +194 -0
- data/lib/testml/compiler/pegex.rb +50 -0
- data/lib/testml/compiler/pegex/ast.rb +145 -0
- data/lib/testml/compiler/pegex/grammar.rb +173 -0
- data/lib/testml/library.rb +7 -0
- data/lib/testml/library/debug.rb +18 -0
- data/lib/testml/library/standard.rb +86 -0
- data/lib/testml/runtime.rb +501 -0
- data/lib/testml/runtime/unit.rb +94 -0
- data/lib/testml/setup.rb +65 -0
- data/lib/testml/util.rb +22 -0
- data/test/ast/arguments.tml +87 -0
- data/test/ast/basic.tml +83 -0
- data/test/ast/dataless.tml +36 -0
- data/test/ast/exceptions.tml +59 -0
- data/test/ast/external.tml +42 -0
- data/test/ast/function.tml +276 -0
- data/test/ast/label.tml +58 -0
- data/test/ast/markers.tml +36 -0
- data/test/ast/semicolons.tml +30 -0
- data/test/ast/truth.tml +85 -0
- data/test/ast/types.tml +163 -0
- data/test/compile-lite.rb +38 -0
- data/test/compile-testml-document.rb +59 -0
- data/test/compile.rb +57 -0
- data/test/inline-bridge.rb +30 -0
- data/test/inline.rb +28 -0
- data/test/strings.rb +24 -0
- data/test/testml.rb +38 -0
- data/test/testml.yaml +10 -0
- data/test/testml/arguments.tml +18 -0
- data/test/testml/assertions.tml +15 -0
- data/test/testml/basic.tml +37 -0
- data/test/testml/dataless.tml +9 -0
- data/test/testml/exceptions.tml +16 -0
- data/test/testml/external.tml +8 -0
- data/test/testml/external1.tml +10 -0
- data/test/testml/external2.tml +3 -0
- data/test/testml/function.tml +82 -0
- data/test/testml/label.tml +24 -0
- data/test/testml/markers.tml +19 -0
- data/test/testml/semicolons.tml +10 -0
- data/test/testml/standard.tml +50 -0
- data/test/testml/truth.tml +22 -0
- data/test/testml/types.tml +24 -0
- data/test/testml_bridge.rb +28 -0
- metadata +69 -4
- data/test/fail.rb +0 -12
data/.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
GemSpec = Gem::Specification.new do |gem|
|
4
4
|
gem.name = 'testml'
|
5
|
-
gem.version = '0.0.
|
5
|
+
gem.version = '0.0.2'
|
6
6
|
gem.license = 'MIT'
|
7
7
|
gem.required_ruby_version = '>= 1.9.1'
|
8
8
|
|
@@ -15,4 +15,6 @@ TestML is an Acmeist testing framework.
|
|
15
15
|
gem.homepage = 'http://testml.org'
|
16
16
|
|
17
17
|
gem.files = `git ls-files`.lines.map{|l|l.chomp}
|
18
|
+
|
19
|
+
gem.add_dependency 'pegex', '>= 0.0.3'
|
18
20
|
end
|
data/CHANGELOG.yaml
CHANGED
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -1,16 +1,41 @@
|
|
1
1
|
= testml - TestML for Ruby
|
2
2
|
|
3
|
-
TestML is an Acmeist
|
3
|
+
TestML is an Acmeist unit test language/framework.
|
4
4
|
|
5
5
|
= Synopsis
|
6
6
|
|
7
|
+
In a file called +test/mytest.rb+:
|
8
|
+
|
7
9
|
require 'testml'
|
8
10
|
|
9
|
-
=
|
11
|
+
TestML.new.testml = <<'.'
|
12
|
+
Plan = 4;
|
13
|
+
|
14
|
+
*x.add(*y) == *sum;
|
15
|
+
*x.mult(*y) == *pruduct;
|
16
|
+
|
17
|
+
=== Test one
|
18
|
+
--- x: 42
|
19
|
+
--- y: 43
|
20
|
+
--- sum: 85
|
21
|
+
--- product: 1806
|
22
|
+
.
|
23
|
+
|
24
|
+
= Description
|
25
|
+
|
26
|
+
Coming soon...
|
27
|
+
|
28
|
+
= TestML::Test API
|
29
|
+
|
30
|
+
Coming soon...
|
31
|
+
|
32
|
+
= About TestML
|
33
|
+
|
34
|
+
TestML subclasses Test::Unit, so it Just Works with your other test code/files
|
35
|
+
and with +rake test+.
|
10
36
|
|
11
|
-
|
12
|
-
now.
|
37
|
+
All you need to do is create a new TestML object in a test file.
|
13
38
|
|
14
39
|
= Copyright
|
15
40
|
|
16
|
-
Copyright (c) 2012 Ingy döt Net. See LICENSE for further details.
|
41
|
+
Copyright (c) 2012, 2013 Ingy döt Net. See LICENSE for further details.
|
data/Rakefile
CHANGED
@@ -11,6 +11,12 @@ DevNull = '2>/dev/null'
|
|
11
11
|
require 'rake'
|
12
12
|
require 'rake/testtask'
|
13
13
|
require 'rake/clean'
|
14
|
+
if File.exists? 'test/testml.yaml'
|
15
|
+
if File.exists? 'lib/rake/testml.rb'
|
16
|
+
$:.unshift "#{Dir.getwd}/lib"
|
17
|
+
end
|
18
|
+
require 'rake/testml'
|
19
|
+
end
|
14
20
|
|
15
21
|
task :default => 'help'
|
16
22
|
|
@@ -18,9 +24,12 @@ CLEAN.include GemDir, GemFile, 'data.tar.gz', 'metadata.gz'
|
|
18
24
|
|
19
25
|
desc 'Run the tests'
|
20
26
|
task :test do
|
27
|
+
load '.env' if File.exists? '.env'
|
21
28
|
Rake::TestTask.new do |t|
|
22
29
|
t.verbose = true
|
23
|
-
t.test_files =
|
30
|
+
t.test_files = ENV['DEV_TEST_FILES'] &&
|
31
|
+
FileList[ENV['DEV_TEST_FILES'].split] ||
|
32
|
+
FileList['test/**/*.rb'].sort
|
24
33
|
end
|
25
34
|
end
|
26
35
|
|
data/ToDo
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
- Constants inside modules
|
2
|
+
- Pretty print ruby data structure
|
3
|
+
- Find current test file
|
4
|
+
|
5
|
+
|
6
|
+
- Code review
|
7
|
+
+ rake testml takes yaml file argument
|
8
|
+
|
9
|
+
+ Check irc
|
10
|
+
+ Turn testml-lite into testml
|
11
|
+
+ Eliminate explicit.rb and plan.rb
|
12
|
+
+ Combine testml.tml and testml-lite.tml
|
13
|
+
+ Port TestML::Runtime(.pm) to ruby
|
14
|
+
- Make test pass using real testml runtime
|
15
|
+
- Make all tests pass
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
+ fix 1 == 1
|
20
|
+
+ move TestML::Compiler to a class
|
21
|
+
+ move TestML::Runtime to a class
|
22
|
+
+ testml.plan = 5
|
23
|
+
+ ~~ Match should use index, not regex
|
24
|
+
+ testml.blocks = [...]
|
25
|
+
+ testml.require_or_skip 'x', 'y', 'z'
|
26
|
+
+ make require_or_skip not exit
|
27
|
+
+ TestML::Test constructor can take hash or block or nothing
|
28
|
+
+ rake/testml/lite
|
29
|
+
+ use test/testml.yaml
|
30
|
+
+ copy tml files from source repo
|
31
|
+
+ generate .rb files from templates
|
32
|
+
+ Eliminate global $TestMLError
|
33
|
+
|
34
|
+
- Support Point as first-class function
|
35
|
+
- Support Set, Get and Del
|
36
|
+
x Support surrogate TestML Function as Ruby function
|
37
|
+
- Add Label support
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
- Make a headless TestML::Test object
|
42
|
+
|
43
|
+
- make work with pegex-rb
|
44
|
+
- make a pegex-tml
|
45
|
+
|
46
|
+
- write doc
|
47
|
+
- look at rdoc and yard
|
48
|
+
- release to rubygems
|
49
|
+
|
50
|
+
- port to perl
|
51
|
+
- port to python
|
52
|
+
- port to perl6
|
53
|
+
|
54
|
+
|
55
|
+
== Ideas
|
56
|
+
- test-converage-rb
|
data/lib/rake/testml.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
##
|
2
|
+
# Usage:
|
3
|
+
# rake testml
|
4
|
+
# rake testml ./test/mytestmlconf.yaml
|
5
|
+
#
|
6
|
+
# This rake task syncs your local testml setup with a testml repository and
|
7
|
+
# creates any needed shim test files.
|
8
|
+
|
9
|
+
require 'testml/setup'
|
10
|
+
|
11
|
+
desc 'Update TestML files.'
|
12
|
+
task :testml do
|
13
|
+
TestML::Setup.new.setup(ARGV[1])
|
14
|
+
end
|
data/lib/testml.rb
CHANGED
@@ -1,3 +1,47 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
class TestML
|
2
|
+
VERSION = '0.0.2'
|
3
|
+
|
4
|
+
attr_accessor :runtime
|
5
|
+
attr_accessor :compiler
|
6
|
+
attr_accessor :bridge
|
7
|
+
attr_accessor :library
|
8
|
+
attr_accessor :testml
|
9
|
+
|
10
|
+
def initialize attributes={}
|
11
|
+
attributes.each { |k,v| self.send "#{k}=", v }
|
12
|
+
yield self if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(*args)
|
16
|
+
set_default_classes
|
17
|
+
@runtime.new(
|
18
|
+
compiler: @compiler,
|
19
|
+
bridge: @bridge,
|
20
|
+
library: @library,
|
21
|
+
testml: @testml,
|
22
|
+
).run(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_default_classes
|
26
|
+
if not @runtime
|
27
|
+
require 'testml/runtime/unit'
|
28
|
+
@runtime = TestML::Runtime::Unit
|
29
|
+
end
|
30
|
+
if not @compiler
|
31
|
+
require 'testml/compiler/pegex'
|
32
|
+
@compiler = TestML::Compiler::Pegex
|
33
|
+
end
|
34
|
+
if not @bridge
|
35
|
+
require 'testml/bridge'
|
36
|
+
@bridge = TestML::Bridge
|
37
|
+
end
|
38
|
+
if not @library
|
39
|
+
require 'testml/library/standard'
|
40
|
+
require 'testml/library/debug'
|
41
|
+
@library = [
|
42
|
+
TestML::Library::Standard,
|
43
|
+
TestML::Library::Debug,
|
44
|
+
]
|
45
|
+
end
|
46
|
+
end
|
3
47
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'testml/runtime'
|
2
|
+
|
3
|
+
class TestML::Compiler
|
4
|
+
|
5
|
+
attr_accessor :code
|
6
|
+
attr_accessor :data
|
7
|
+
attr_accessor :text
|
8
|
+
attr_accessor :directives
|
9
|
+
attr_accessor :function
|
10
|
+
|
11
|
+
def compile(input)
|
12
|
+
preprocess(input, 'top')
|
13
|
+
compile_code
|
14
|
+
compile_data
|
15
|
+
|
16
|
+
if @directives['DumpAST']
|
17
|
+
XXX @function
|
18
|
+
end
|
19
|
+
|
20
|
+
@function.namespace ||= {}
|
21
|
+
@function.namespace['TestML'] = @directives['TestML']
|
22
|
+
|
23
|
+
@function.outer = TestML::Function.new
|
24
|
+
return @function
|
25
|
+
end
|
26
|
+
|
27
|
+
def preprocess(input, top=nil)
|
28
|
+
parts = input.split /^((?:\%\w+.*|\#.*|\ *)\n)/
|
29
|
+
input = ''
|
30
|
+
|
31
|
+
@directives = {
|
32
|
+
'TestML' => nil,
|
33
|
+
'DataMarker' => nil,
|
34
|
+
'BlockMarker' => '===',
|
35
|
+
'PointMarker' => '---',
|
36
|
+
}
|
37
|
+
|
38
|
+
order_error = false
|
39
|
+
parts.each do |part|
|
40
|
+
next if part.empty?
|
41
|
+
if part =~ /^(\#.*|\ *)\n/
|
42
|
+
input << "\n"
|
43
|
+
next
|
44
|
+
end
|
45
|
+
if part =~ /^%(\w+)\s*(.*?)\s*\n/
|
46
|
+
directive, value = $1, $2
|
47
|
+
input << "\n"
|
48
|
+
if directive == 'TestML'
|
49
|
+
fail "Invalid TestML directive" \
|
50
|
+
unless value =~ /^\d+\.\d+\.\d+$/
|
51
|
+
fail "More than one TestML directive found" \
|
52
|
+
if @directives['TestML']
|
53
|
+
@directives['TestML'] = TestML::Str.new(value)
|
54
|
+
next
|
55
|
+
end
|
56
|
+
order_error = true unless @directives['TestML']
|
57
|
+
if directive == 'Include'
|
58
|
+
runtime = $TestMLRuntimeSingleton \
|
59
|
+
or fail "Can't process Include. No runtime available"
|
60
|
+
include_ = self.class.new
|
61
|
+
include_.preprocess(runtime.read_testml_file(value))
|
62
|
+
input << include_.text
|
63
|
+
@directives['DataMarker'] =
|
64
|
+
include_.directives['DataMarker']
|
65
|
+
@directives['BlockMarker'] =
|
66
|
+
include_.directives['BlockMarker']
|
67
|
+
@directives['PointMarker'] =
|
68
|
+
include_.directives['PointMarker']
|
69
|
+
fail "Can't define %TestML in an Included file" \
|
70
|
+
if include_.directives['TestML']
|
71
|
+
elsif directive =~ /^(DataMarker|BlockMarker|PointMarker)$/
|
72
|
+
@directives[directive] = value
|
73
|
+
elsif directive =~ /^(DebugPegex|DumpAST)$/
|
74
|
+
value = true if value.empty?
|
75
|
+
@directives[directive] = value
|
76
|
+
else
|
77
|
+
fail "Unknown TestML directive '$#{directive}'"
|
78
|
+
end
|
79
|
+
else
|
80
|
+
order_error = true if !input.empty? and !@directives['TestML']
|
81
|
+
input << part
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if top
|
86
|
+
fail "No TestML directive found" \
|
87
|
+
unless @directives['TestML']
|
88
|
+
fail "%TestML directive must be the first (non-comment) statement" \
|
89
|
+
if order_error
|
90
|
+
|
91
|
+
@directives['DataMarker'] ||= @directives['BlockMarker']
|
92
|
+
if split = input.index("\n#{@directives['DataMarker']}")
|
93
|
+
@code = input[0..(split)]
|
94
|
+
@data = input[(split + 1)..-1]
|
95
|
+
else
|
96
|
+
@code = input
|
97
|
+
@data = ''
|
98
|
+
end
|
99
|
+
|
100
|
+
@code.gsub! /^\\(\\*[\%\#])/, '\1'
|
101
|
+
@data.gsub! /^\\(\\*[\%\#])/, '\1'
|
102
|
+
else
|
103
|
+
@text = input
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'testml/compiler'
|
2
|
+
|
3
|
+
class TestML::Compiler::Lite < TestML::Compiler
|
4
|
+
require 'testml/runtime'
|
5
|
+
|
6
|
+
attr_accessor :input
|
7
|
+
attr_accessor :points
|
8
|
+
attr_accessor :tokens
|
9
|
+
attr_accessor :function
|
10
|
+
|
11
|
+
WS = %r!\s+!
|
12
|
+
ANY = %r!.!
|
13
|
+
STAR = %r!\*!
|
14
|
+
NUM = %r!-?[0-9]+!
|
15
|
+
WORD = %r!\w+!
|
16
|
+
HASH = %r!#!
|
17
|
+
EQ = %r!=!
|
18
|
+
TILDE = %r!~!
|
19
|
+
LP = %r!\(!
|
20
|
+
RP = %r!\)!
|
21
|
+
DOT = %r!\.!
|
22
|
+
COMMA = %r!,!
|
23
|
+
SEMI = %r!;!
|
24
|
+
SSTR = %r!'(?:[^']*)'!
|
25
|
+
DSTR = %r!"(?:[^"]*)"!
|
26
|
+
ENDING = %r!(?:#{RP}|#{COMMA}|#{SEMI})!
|
27
|
+
|
28
|
+
POINT = %r!#{STAR}#{WORD}!
|
29
|
+
QSTR = %r!(?:#{SSTR}|#{DSTR})!
|
30
|
+
COMP = %r!(?:#{EQ}#{EQ}|#{TILDE}#{TILDE})!
|
31
|
+
OPER = %r!(?:#{COMP}|#{EQ})!
|
32
|
+
PUNCT = %r!(?:#{LP}|#{RP}|#{DOT}|#{COMMA}|#{SEMI})!
|
33
|
+
|
34
|
+
TOKENS = %r!(?:#{POINT}|#{NUM}|#{WORD}|#{QSTR}|#{PUNCT}|#{OPER})!
|
35
|
+
|
36
|
+
def compile_code
|
37
|
+
@function = TestML::Function.new
|
38
|
+
while not @code.empty? do
|
39
|
+
@code.sub! /^(.*)(\r\n|\n|)/, ''
|
40
|
+
@line = $1
|
41
|
+
tokenize
|
42
|
+
next if done
|
43
|
+
parse_assignment ||
|
44
|
+
parse_assertion ||
|
45
|
+
fail_()
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def tokenize
|
50
|
+
@tokens = []
|
51
|
+
while not @line.empty? do
|
52
|
+
next if @line.sub!(/^#{WS}/, '')
|
53
|
+
next if @line.sub!(/^#{HASH}#{ANY}*/, '')
|
54
|
+
if @line.sub!(/^(#{TOKENS})/, '')
|
55
|
+
@tokens.push $1
|
56
|
+
else
|
57
|
+
fail_("Failed to get token here: '#{@line}'")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse_assignment
|
63
|
+
return unless peek(2) == '='
|
64
|
+
var, op = pop(2)
|
65
|
+
expr = parse_expression
|
66
|
+
pop if !done and peek == ';'
|
67
|
+
fail_() unless done
|
68
|
+
@function.statements.push TestML::Assignment.new(var, expr)
|
69
|
+
return true
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_assertion
|
73
|
+
return unless @tokens.grep /^#{COMP}$/
|
74
|
+
@points = []
|
75
|
+
left = parse_expression
|
76
|
+
token = pop
|
77
|
+
op =
|
78
|
+
token == '==' ? 'EQ' :
|
79
|
+
token == '~~' ? 'HAS' :
|
80
|
+
fail_
|
81
|
+
right = parse_expression
|
82
|
+
pop if !done and peek == ';'
|
83
|
+
fail_ unless done
|
84
|
+
|
85
|
+
@function.statements.push TestML::Statement.new(
|
86
|
+
left,
|
87
|
+
TestML::Assertion.new(
|
88
|
+
op,
|
89
|
+
right,
|
90
|
+
),
|
91
|
+
points.empty? ? nil : points
|
92
|
+
)
|
93
|
+
return true
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_expression
|
97
|
+
calls = []
|
98
|
+
while !done and peek !~ /^(#{ENDING}|#{COMP})$/ do
|
99
|
+
token = pop
|
100
|
+
if token =~ /^#{NUM}$/
|
101
|
+
calls.push TestML::Num.new(token.to_i)
|
102
|
+
elsif token =~ /^#{QSTR}$/
|
103
|
+
str = token[1..-2]
|
104
|
+
calls.push TestML::Str.new(str)
|
105
|
+
elsif token =~ /^#{WORD}$/
|
106
|
+
call = TestML::Call.new(token)
|
107
|
+
if !done and peek == '('
|
108
|
+
call.args = parse_args
|
109
|
+
end
|
110
|
+
calls.push call
|
111
|
+
elsif token =~ /^#{POINT}$/
|
112
|
+
token =~ /(#{WORD})/ or fail
|
113
|
+
points.push $1
|
114
|
+
calls.push TestML::Point.new($1)
|
115
|
+
else
|
116
|
+
fail_("Unknown token '#{token}'")
|
117
|
+
end
|
118
|
+
if !done and peek == '.'
|
119
|
+
pop
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
return calls.size == 1 \
|
124
|
+
? calls[0]
|
125
|
+
: TestML::Expression.new(calls)
|
126
|
+
end
|
127
|
+
|
128
|
+
def parse_args
|
129
|
+
pop == '(' or fail
|
130
|
+
args = []
|
131
|
+
while peek != ')' do
|
132
|
+
args.push parse_expression
|
133
|
+
pop if peek == ','
|
134
|
+
end
|
135
|
+
pop
|
136
|
+
return args
|
137
|
+
end
|
138
|
+
|
139
|
+
def compile_data
|
140
|
+
input = @data
|
141
|
+
input.gsub! /^#.*\n/, "\n"
|
142
|
+
input.gsub! /^\\/, ''
|
143
|
+
blocks = input.split(/(^===.*?(?=^===|\z))/m).select{|el|!el.empty?}
|
144
|
+
blocks.each{|block| block.sub! /\n+\z/, "\n"}
|
145
|
+
|
146
|
+
data = []
|
147
|
+
blocks.each do |string_block|
|
148
|
+
block = TestML::Block.new
|
149
|
+
string_block.gsub! /\A===\ +(.*?)\ *\n/, '' or
|
150
|
+
fail "No block label! #{string_block}"
|
151
|
+
block.label = $1
|
152
|
+
while !string_block.empty? do
|
153
|
+
next if string_block.sub! /\A\n+/, ''
|
154
|
+
key, value = nil, nil
|
155
|
+
if string_block.gsub!(/\A---\ +(\w+):\ +(.*)\n/, '') or
|
156
|
+
string_block.gsub!(/\A---\ +(\w+)\n(.*?)(?=^---|\z)/m, '')
|
157
|
+
key, value = $1, $2
|
158
|
+
else
|
159
|
+
fail "Failed to parse TestML string:\n#{string_block}"
|
160
|
+
end
|
161
|
+
block.points ||= {}
|
162
|
+
block.points[key] = value
|
163
|
+
|
164
|
+
if key =~ /^(ONLY|SKIP|LAST)$/
|
165
|
+
block.points[key] = true
|
166
|
+
end
|
167
|
+
end
|
168
|
+
data.push block
|
169
|
+
end
|
170
|
+
@function.data = data unless data.empty?
|
171
|
+
end
|
172
|
+
|
173
|
+
def done
|
174
|
+
tokens.empty?
|
175
|
+
end
|
176
|
+
|
177
|
+
def peek(index=1)
|
178
|
+
fail if index > @tokens.size
|
179
|
+
@tokens[index - 1]
|
180
|
+
end
|
181
|
+
|
182
|
+
def pop(count=1)
|
183
|
+
fail if count > @tokens.size
|
184
|
+
array = @tokens.slice! 0..(count-1)
|
185
|
+
count > 1 ? array : array[0]
|
186
|
+
end
|
187
|
+
|
188
|
+
def fail_(message=nil)
|
189
|
+
text = "Failed to compile TestML document.\n"
|
190
|
+
text << "Reason: #{message}\n" if message
|
191
|
+
text << "\nCode section of failure:\n#{@line}\n#{@code}\n"
|
192
|
+
fail text
|
193
|
+
end
|
194
|
+
end
|