testml 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|