rdx 0.9.0.pre
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/.rdx +20 -0
- data/README +19 -0
- data/bin/rdx +7 -0
- data/examples/minimal/.rdx +8 -0
- data/examples/minimal/README +10 -0
- data/examples/minimal/lib/other_conventions.rb +64 -0
- data/examples/minimal/lib/the_basics.rb +94 -0
- data/examples/minimal/lib/using_directives.rb +66 -0
- data/examples/minimal/rakefile +27 -0
- data/examples/ruby-2.0.0-p0/README +7 -0
- data/examples/ruby-2.0.0-p0/install/core/.rdx +6 -0
- data/examples/ruby-2.0.0-p0/install/core/README +19 -0
- data/examples/ruby-2.0.0-p0/install/core/Rakefile +61 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/array.c.diff +166 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/bignum.c.diff +11 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/class.c.diff +36 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/compar.c.diff +11 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/complex.c.diff +301 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/cont.c.diff +65 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/dir.c.diff +147 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/re.rdoc.diff +328 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/security.rdoc.diff +8 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/standard_library.rdoc.diff +0 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax.rdoc.diff +0 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/assignment.rdoc.diff +160 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/calling_methods.rdoc.diff +130 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/control_expressions.rdoc.diff +254 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/exceptions.rdoc.diff +0 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/literals.rdoc.diff +54 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/methods.rdoc.diff +157 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/miscellaneous.rdoc.diff +91 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/modules_and_classes.rdoc.diff +161 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/precedence.rdoc.diff +8 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/refinements.rdoc.diff +146 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/encoding.c.diff +276 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/enum.c.diff +281 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/enumerator.c.diff +479 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/error.c.diff +143 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/eval.c.diff +47 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/eval_jump.c.diff +23 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/file.c.diff +752 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/gc.c.diff +195 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/hash.c.diff +84 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/iseq.c.diff +354 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/load.c.diff +53 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/marshal.c.diff +98 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/math.c.diff +110 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/numeric.c.diff +103 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/object.c.diff +295 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/pack.c.diff +18 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/parse.y.diff +23 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/proc.c.diff +155 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/random.c.diff +126 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/range.c.diff +49 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/rational.c.diff +312 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/re.c.diff +207 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/ruby.c.diff +21 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/signal.c.diff +67 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/sprintf.c.diff +29 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/string.c.diff +73 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/struct.c.diff +20 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/time.c.diff +691 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/transcode.c.diff +435 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/variable.c.diff +62 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/vm_backtrace.c.diff +164 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/vm_eval.c.diff +99 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/vm_method.c.diff +17 -0
- data/examples/ruby-2.0.0-p0/install/core/diffs/vm_trace.c.diff +393 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/.rdx +6 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/README +19 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/Rakefile +53 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/abbrev.rb.diff +77 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/base64.rb.diff +42 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/benchmark.rb.diff +144 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/cmath.rb.diff +52 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/forwardable.rb.diff +150 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/mathn.rb.diff +58 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/matrix.rb.diff +657 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/observer.rb.diff +31 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/optparse.rb.diff +147 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/ostruct.rb.diff +78 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/prime.rb.diff +52 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/pstore.rb.diff +110 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/scanf.rb.diff +100 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/securerandom.rb.diff +144 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/set.rb.diff +637 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/shellwords.rb.diff +66 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/singleton.rb.diff +37 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/tempfile.rb.diff +104 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/thread.rb.diff +38 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/time.rb.diff +140 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/tmpdir.rb.diff +52 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/uri.rb.diff +39 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/uri/common.rb.diff +237 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/weakref.rb.diff +36 -0
- data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/yaml/store.rb.diff +27 -0
- data/examples/ruby-2.0.0-p0/rakefile +165 -0
- data/lib/rdx.rb +331 -0
- data/lib/rdx/assertions.rb +484 -0
- data/lib/rdx/binding.rb +151 -0
- data/lib/rdx/code_object.rb +598 -0
- data/lib/rdx/comment.rb +338 -0
- data/lib/rdx/convention.rb +1174 -0
- data/lib/rdx/directive.rb +1432 -0
- data/lib/rdx/example.rb +679 -0
- data/lib/rdx/generator.rb +112 -0
- data/lib/rdx/generator/rdoc.rb +1006 -0
- data/lib/rdx/options.rb +359 -0
- data/lib/rdx/plain_text.rb +65 -0
- data/lib/rdx/reporter.rb +421 -0
- data/lib/rdx/ruby_lex.rb +324 -0
- data/lib/rdx/runner.rb +309 -0
- data/lib/rdx/source_file.rb +94 -0
- data/lib/rdx/specification.rb +194 -0
- data/lib/rdx/statement.rb +248 -0
- data/lib/rdx/store.rb +119 -0
- data/lib/rdx/task.rb +361 -0
- data/lib/rdx/text.rb +688 -0
- data/lib/rdx/version.rb +15 -0
- data/rakefile +64 -0
- metadata +203 -0
data/lib/rdx/binding.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
|
2
|
+
module RDX
|
3
|
+
|
4
|
+
# The _main_ object of the whole application
|
5
|
+
MAIN_OBJECT = TOPLEVEL_BINDING.eval 'self' # :nodoc:
|
6
|
+
|
7
|
+
#
|
8
|
+
# The methods defined here provide some useful +Binding+ objects, that can be used to execute some code.
|
9
|
+
#
|
10
|
+
module Binding
|
11
|
+
|
12
|
+
extend self
|
13
|
+
|
14
|
+
if false # For RDoc, methods will be defined after
|
15
|
+
|
16
|
+
#
|
17
|
+
# Gives a +clone+ of +TOPLEVEL_BINDING+.
|
18
|
+
# The code executed here can affect very easily the rest of application (only local variables are
|
19
|
+
# not propagated), so care must be taken.
|
20
|
+
#
|
21
|
+
# :rdx: toplevel
|
22
|
+
#
|
23
|
+
# # Some general proprieties:
|
24
|
+
# b = RDX::Binding.toplevel
|
25
|
+
# b.eval "local_variables" # => [] # The binding is initially clear
|
26
|
+
# b==RDX::Binding.toplevel # => false # Each binding carries its local variables
|
27
|
+
# b_self = b.eval "self" # > main
|
28
|
+
# # That self looks like the main...
|
29
|
+
# self == b_self # => true # ...and it is! Instance variables (and singleton methods) will be shared
|
30
|
+
# b.eval "Module.nesting" # => [] # Acts like [Object], so toplevel definitions are global (shared)
|
31
|
+
#
|
32
|
+
# # Interactions between them:
|
33
|
+
# b1 = RDX::Binding.toplevel
|
34
|
+
# b1.eval "local_var = @ivar = CONST = true"
|
35
|
+
# b2 = RDX::Binding.toplevel
|
36
|
+
# b1 == b2 #=> false # Local variables will be different
|
37
|
+
# b2.eval "local_var" # raises NameError # Ok, this isn't seen!
|
38
|
+
# # But these two are:
|
39
|
+
# b2.eval "@ivar" # => true
|
40
|
+
# b2.eval "CONST" # => true
|
41
|
+
#
|
42
|
+
# # In true toplevel:
|
43
|
+
# local_var # raises NameError
|
44
|
+
# @ivar # => true
|
45
|
+
# CONST # => true
|
46
|
+
# # Watch out from pollution! Clean up steps:
|
47
|
+
# remove_instance_variable :@ivar # => true
|
48
|
+
# Object.send :remove_const, :CONST # => true
|
49
|
+
#
|
50
|
+
def toplevel_binding
|
51
|
+
# see this source file...
|
52
|
+
end
|
53
|
+
alias toplevel toplevel_binding
|
54
|
+
|
55
|
+
#
|
56
|
+
# Gives the toplevel +binding+ of a main-like object.
|
57
|
+
# The aim is to emulate the toplevel binding without polluting it too much.
|
58
|
+
#
|
59
|
+
# Specifically:
|
60
|
+
# * +self+ points to a clone of the main object;
|
61
|
+
# * the _lexical_ _scope_ is set to this main object's singleton class;
|
62
|
+
# * +include+ and +define_method+ are redefined to better reproduce this environment.
|
63
|
+
#
|
64
|
+
# These lead to the fact that almost all new definitions - intentional top-level constants and global
|
65
|
+
# variables are excluded - are restricted to that binding. All of this without sacrifycing efficiency.
|
66
|
+
#
|
67
|
+
# :rdx: toplevel
|
68
|
+
#
|
69
|
+
# # Some general proprieties:
|
70
|
+
# b = RDX::Binding.pseudo_toplevel
|
71
|
+
# b.eval "local_variables" # => [] # The binding is initially clear
|
72
|
+
# b==RDX::Binding.pseudo_toplevel # => false # So each binding carries its local variables
|
73
|
+
# b_self = b.eval "self" # > main
|
74
|
+
# # That self looks like the main...
|
75
|
+
# self == b_self # => false # ...but it isn't! Instance variables will NOT be shared
|
76
|
+
# b_meta = class << b_self; self; end
|
77
|
+
# b.eval "Module.nesting" # => [b_meta] # Constants will be defined inside that (NOT shared)
|
78
|
+
#
|
79
|
+
# # Interactions between them:
|
80
|
+
# b1 = RDX::Binding.pseudo_toplevel
|
81
|
+
# b1.eval "local_var = @ivar = CONST = true"
|
82
|
+
# b2 = RDX::Binding.pseudo_toplevel
|
83
|
+
# b2.eval "local_var" # raises NameError
|
84
|
+
# b2.eval "@ivar" # => nil
|
85
|
+
# b2.eval "CONST" # raises NameError # Constants are also hidden!
|
86
|
+
#
|
87
|
+
# # The same is true for the true toplevel:
|
88
|
+
# local_var # raises NameError
|
89
|
+
# @ivar # => nil
|
90
|
+
# CONST # raises NameError
|
91
|
+
#
|
92
|
+
# # Intentional polluting:
|
93
|
+
# b1.eval "::TOP_LEVEL_CONST = $global_var = true"
|
94
|
+
# TOP_LEVEL_CONST # => true
|
95
|
+
# $global_var # => true
|
96
|
+
# # Clean up:
|
97
|
+
# Object.send :remove_const, :TOP_LEVEL_CONST # => true
|
98
|
+
# $global_var = nil # Even if it isn't truely removed...
|
99
|
+
#
|
100
|
+
# These features makes #pseudo_toplevel_binding the favourite by RDX.
|
101
|
+
#
|
102
|
+
def pseudo_toplevel_binding
|
103
|
+
# see this source file...
|
104
|
+
end
|
105
|
+
alias pseudo_toplevel pseudo_toplevel_binding
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
# :enddoc:
|
115
|
+
|
116
|
+
# This is empty for sure - we don't know what it was done with +TOPLEVEL_BINDING+
|
117
|
+
RDX::TOPLEVEL_BINDING = binding().clone # :nodoc:
|
118
|
+
|
119
|
+
# The use of Module#module_eval and Module.new doesn't pollute Module.nesting
|
120
|
+
# for the new binding.
|
121
|
+
RDX.module_eval do
|
122
|
+
|
123
|
+
self::Binding.module_eval do
|
124
|
+
|
125
|
+
def toplevel_binding
|
126
|
+
RDX::TOPLEVEL_BINDING.clone
|
127
|
+
end
|
128
|
+
alias toplevel toplevel_binding
|
129
|
+
|
130
|
+
def pseudo_toplevel_binding
|
131
|
+
# The special behaviour of this _main_ object all lives in its singleton class,
|
132
|
+
# so first +clone+ is used on it to ensure all properties (except uniqueness) are copied.
|
133
|
+
RDX::MAIN_OBJECT.clone.instance_eval do
|
134
|
+
# override methods that use +top_wrapper+ (set by Kernel#load) forcing them to use the
|
135
|
+
# lexical scope instead of Object. This works fine in the toplevel, but we cannot gain the
|
136
|
+
# temporary mixin in Object...
|
137
|
+
class << self # :nodoc:
|
138
|
+
alias include extend
|
139
|
+
private :include
|
140
|
+
alias define_method define_singleton_method
|
141
|
+
private :define_method
|
142
|
+
end
|
143
|
+
self # this is +top_self+
|
144
|
+
# instance_eval sets the lexical scope to its singleton class, which is what we want
|
145
|
+
end.instance_eval 'binding' # the block remembers the context...
|
146
|
+
end
|
147
|
+
alias pseudo_toplevel pseudo_toplevel_binding
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
@@ -0,0 +1,598 @@
|
|
1
|
+
|
2
|
+
module RDX
|
3
|
+
|
4
|
+
#
|
5
|
+
# Files are parsed and subdivided into code objects.
|
6
|
+
#
|
7
|
+
# Each code object is represented by a class that has CodeObject in its
|
8
|
+
# +ancestors+ list. Here is the inheritance tree structure:
|
9
|
+
# * CodeObject (abstract)
|
10
|
+
# * SourceFile
|
11
|
+
# * PlainText
|
12
|
+
# * Directive
|
13
|
+
# * CodeObject::Runnable (abstract)
|
14
|
+
# * Statement
|
15
|
+
# * Expectation
|
16
|
+
# * CodeObject::Runnable::Startable (abstract)
|
17
|
+
# * Comment
|
18
|
+
# * Example
|
19
|
+
# However this hierarchy is useful only for implementation details.
|
20
|
+
#
|
21
|
+
# The concrete objects are organized (hence better represented) in the following way,
|
22
|
+
# where each one have one #parent and an +Array+ of #children.
|
23
|
+
# * SourceFile
|
24
|
+
# * Comment
|
25
|
+
# * PlainText
|
26
|
+
# * Directive
|
27
|
+
# * Example
|
28
|
+
# * Statement
|
29
|
+
# * Expectation
|
30
|
+
#
|
31
|
+
class CodeObject
|
32
|
+
|
33
|
+
include RDX::Util
|
34
|
+
|
35
|
+
def self.delegate_to_parent name # :nodoc:
|
36
|
+
define_method name do |*args,&blk|
|
37
|
+
parent && parent.__send__(name,*args,&blk)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
private_class_method :delegate_to_parent
|
41
|
+
|
42
|
+
# +String+ containing the text.
|
43
|
+
attr_reader :text
|
44
|
+
|
45
|
+
# Absolute line number in the source file.
|
46
|
+
attr_reader :line_no
|
47
|
+
alias first_line_no line_no
|
48
|
+
|
49
|
+
# +Hash+ of arbitrary metadata.
|
50
|
+
attr_reader :metadata
|
51
|
+
|
52
|
+
def initialize text, relative_line_no=nil # :nodoc:
|
53
|
+
if @text=text and Object.const_defined?(:Encoding)
|
54
|
+
# Allow representation of multibyte characters in US-ASCII source (eg: "\x80\u3042")
|
55
|
+
# TODO: see the magic comment instead...
|
56
|
+
text.force_encoding('UTF-8') if text.encoding == Encoding::US_ASCII
|
57
|
+
end
|
58
|
+
@line_no = parent.line_no + relative_line_no - 1 if relative_line_no
|
59
|
+
@metadata = {}
|
60
|
+
end
|
61
|
+
|
62
|
+
# The parent CodeObject (at the top is +nil+).
|
63
|
+
def parent
|
64
|
+
end
|
65
|
+
|
66
|
+
# The ancestor list of CodeObject, starting from +self+.
|
67
|
+
def ancestors
|
68
|
+
pos = self
|
69
|
+
result = [pos]
|
70
|
+
while pos = pos.parent
|
71
|
+
result << pos
|
72
|
+
end
|
73
|
+
result
|
74
|
+
end
|
75
|
+
|
76
|
+
# +Array+ of children CodeObject.
|
77
|
+
def children
|
78
|
+
[]
|
79
|
+
end
|
80
|
+
|
81
|
+
# The absolute path of the source file.
|
82
|
+
def file_name
|
83
|
+
source_file.file_name
|
84
|
+
end
|
85
|
+
|
86
|
+
# The count of text lines.
|
87
|
+
def line_count
|
88
|
+
text.count("\n")
|
89
|
+
end
|
90
|
+
|
91
|
+
# The absolute line number of the last line.
|
92
|
+
def last_line_no
|
93
|
+
first_line_no + line_count - 1
|
94
|
+
end
|
95
|
+
|
96
|
+
# The +Range+ of absolute line number covering the text.
|
97
|
+
def line_range
|
98
|
+
first_line_no .. last_line_no
|
99
|
+
end
|
100
|
+
|
101
|
+
# Line number relative to the parent code object
|
102
|
+
def relative_line_no
|
103
|
+
self.line_no - parent.line_no + 1
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Makes the location string <tt>"file_name:line_no"</tt>.
|
108
|
+
# If _l_no_ is given it will be added to #line_no.
|
109
|
+
# If _text_ is given the location string will also contain <tt>":in `text'"</tt>.
|
110
|
+
#
|
111
|
+
def location l_no=nil, text=nil
|
112
|
+
line_no = self.line_no
|
113
|
+
return nil unless file_name && line_no
|
114
|
+
line_no += l_no-1 if l_no
|
115
|
+
text &&= ":in `#{text}'"
|
116
|
+
return "#{file_name}:#{line_no}#{text}"
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# :method:
|
121
|
+
# The Comment of the enclosing lexical scope (may be +nil+).
|
122
|
+
delegate_to_parent :enclosing_comment
|
123
|
+
|
124
|
+
#
|
125
|
+
# :section: API
|
126
|
+
#
|
127
|
+
|
128
|
+
# Generates a warning with the given _msg_, _type_ and _frame_.
|
129
|
+
# If the _type_ isn't disabled it will be registered in the report.
|
130
|
+
def warn msg=nil, type=nil, frame=nil
|
131
|
+
msg ||= 'No message given'
|
132
|
+
frame ||= location
|
133
|
+
register Warning.new(msg,type,frame) unless warning_disabled?(type)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Registers a skip in the report with the given _msg_, _type_ and _frame_.
|
137
|
+
def skip msg=nil, type=nil, frame=nil
|
138
|
+
msg ||= 'No message given'
|
139
|
+
frame ||= location
|
140
|
+
register Skip.new(msg,type,frame)
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# Disables the given _types_ of warning - all of them if types contains +:all+ or it's empty.
|
145
|
+
# The type of a generated warning is showed in the report: once seen, you know
|
146
|
+
# how to disable it if you want.
|
147
|
+
#
|
148
|
+
def disable_warnings *types
|
149
|
+
types.map!{ |s| s.to_s.downcase.to_sym }
|
150
|
+
types << :all if types.empty?
|
151
|
+
(metadata[:disabled_warnings] ||= []).concat types
|
152
|
+
end
|
153
|
+
|
154
|
+
# Skip the #build phase for this object.
|
155
|
+
def skip_building
|
156
|
+
define_singleton_method(:build){ |*| }
|
157
|
+
end
|
158
|
+
|
159
|
+
# :stopdoc:
|
160
|
+
|
161
|
+
undef binding
|
162
|
+
undef eval
|
163
|
+
|
164
|
+
def location_ary
|
165
|
+
[file_name,line_no]
|
166
|
+
end
|
167
|
+
|
168
|
+
def <=> other
|
169
|
+
return nil unless other.respond_to?(:location_ary)
|
170
|
+
return self.location_ary <=> other.location_ary
|
171
|
+
end
|
172
|
+
|
173
|
+
def warning_disabled? type
|
174
|
+
disabled_warnings = metadata[:disabled_warnings]
|
175
|
+
return true if disabled_warnings and
|
176
|
+
disabled_warnings.include?(:all) || disabled_warnings.include?(type)
|
177
|
+
return parent && parent.warning_disabled?(type)
|
178
|
+
end
|
179
|
+
|
180
|
+
def register reportable
|
181
|
+
source_file.register reportable, self
|
182
|
+
end
|
183
|
+
|
184
|
+
def register_error err, in_building=true
|
185
|
+
err = UnexpectedError.new(err)
|
186
|
+
err.in_building = in_building
|
187
|
+
register err
|
188
|
+
end
|
189
|
+
|
190
|
+
def store
|
191
|
+
runner.store
|
192
|
+
end
|
193
|
+
|
194
|
+
def encoding= enc
|
195
|
+
return unless enc && text
|
196
|
+
text.force_encoding enc
|
197
|
+
children.each{ |child| child.encoding = enc }
|
198
|
+
end
|
199
|
+
|
200
|
+
delegate_to_parent :starter
|
201
|
+
|
202
|
+
def to_s
|
203
|
+
"#<%s@%s>" % [self.class, location]
|
204
|
+
end
|
205
|
+
|
206
|
+
def inspect
|
207
|
+
content = text || ''
|
208
|
+
if content.size > 60
|
209
|
+
content = content[0..60] + '...'
|
210
|
+
end
|
211
|
+
"#<%s:0x%x@%s;%p>" % [self.class, __id__, location, content]
|
212
|
+
end
|
213
|
+
|
214
|
+
# :startdoc:
|
215
|
+
|
216
|
+
#
|
217
|
+
# :section: Internal
|
218
|
+
#
|
219
|
+
|
220
|
+
#
|
221
|
+
# First calls #build_self; if something gets wrong the error
|
222
|
+
# is reported and rescued, so that it will not propagate and
|
223
|
+
# the other brothers can anyhow be built.
|
224
|
+
# Then calls #build_children.
|
225
|
+
#
|
226
|
+
def build
|
227
|
+
build_self
|
228
|
+
rescue *Assertions::PASSTHROUGH_EXCEPTIONS
|
229
|
+
raise
|
230
|
+
rescue Exception => err
|
231
|
+
unless err.backtrace.frozen?
|
232
|
+
klassname = self.class.name.split('::').last.downcase
|
233
|
+
err.message.insert 0, "Error, #{klassname} skipped:\n"
|
234
|
+
l_no = err.line_no if err.is_a?(ParseError)
|
235
|
+
err.backtrace.unshift(location l_no)
|
236
|
+
end
|
237
|
+
register_error err
|
238
|
+
skip_running
|
239
|
+
else
|
240
|
+
build_children
|
241
|
+
return self
|
242
|
+
end
|
243
|
+
|
244
|
+
# This must set up #children.
|
245
|
+
def build_self # :doc:
|
246
|
+
end
|
247
|
+
private :build_self
|
248
|
+
|
249
|
+
# Calls #build for each child.
|
250
|
+
def build_children # :doc:
|
251
|
+
children.each(&:build)
|
252
|
+
end
|
253
|
+
private :build_children
|
254
|
+
|
255
|
+
# Adds _frame_ to the exception raised by the block.
|
256
|
+
def trace_execution frame=location
|
257
|
+
yield
|
258
|
+
rescue Exception => err
|
259
|
+
if !err.is_a?(Assertion) && # Backtrace already handled in trace_assertion
|
260
|
+
!err.backtrace.frozen? && # Exception raised from the code: already tracked
|
261
|
+
err.backtrace.first != frame # Redundant mark
|
262
|
+
err.set_backtrace err.backtrace.unshift(frame.to_str)
|
263
|
+
end
|
264
|
+
raise err
|
265
|
+
end
|
266
|
+
protected :trace_execution
|
267
|
+
|
268
|
+
|
269
|
+
|
270
|
+
#
|
271
|
+
# A CodeObject that RDX may run.
|
272
|
+
#
|
273
|
+
class Runnable < self
|
274
|
+
|
275
|
+
# Delegates to the parent (see Runnable::Startable#binding).
|
276
|
+
def binding
|
277
|
+
parent.respond_to?(:binding) && parent.binding
|
278
|
+
end
|
279
|
+
|
280
|
+
# Delegates to the parent (see Runnable::Startable#toplevel_self).
|
281
|
+
def toplevel_self
|
282
|
+
parent.respond_to?(:toplevel_self) && parent.toplevel_self
|
283
|
+
end
|
284
|
+
|
285
|
+
# Delegates to the parent (see Runnable::Startable#toplevel_scope).
|
286
|
+
def toplevel_scope
|
287
|
+
parent.respond_to?(:toplevel_scope) && parent.toplevel_scope
|
288
|
+
end
|
289
|
+
|
290
|
+
#
|
291
|
+
# :section: API
|
292
|
+
#
|
293
|
+
|
294
|
+
# Skip the #run phase for this object.
|
295
|
+
def skip_running
|
296
|
+
define_singleton_method(:run){ |*| }
|
297
|
+
end
|
298
|
+
|
299
|
+
#
|
300
|
+
# :section: Internal
|
301
|
+
#
|
302
|
+
|
303
|
+
# Runs in turn each child. This method can be patched and extended with other actions.
|
304
|
+
def run
|
305
|
+
children.each(&:whole_run)
|
306
|
+
end
|
307
|
+
|
308
|
+
# By default delegates to #run but can be wrapped with other actions.
|
309
|
+
def whole_run
|
310
|
+
run
|
311
|
+
end
|
312
|
+
|
313
|
+
# Registers _frame_ as a failure if the block fails an assertion;
|
314
|
+
# registers a success otherwise.
|
315
|
+
def trace_assertion conv_name=nil, frame=location
|
316
|
+
trace_execution frame do # handles possible exceptions raised during the assertion
|
317
|
+
begin
|
318
|
+
yield
|
319
|
+
rescue Assertion => fail
|
320
|
+
register_failure fail, conv_name, frame
|
321
|
+
# Assertions do not propagate so that the rest can run
|
322
|
+
else # No failure, no error
|
323
|
+
register_success conv_name, frame
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
protected :trace_assertion
|
328
|
+
|
329
|
+
# Providing meaningful default values things can stay apparently simple.
|
330
|
+
def eval code, binding=binding, filename=file_name, lineno=line_no # :doc:
|
331
|
+
code = code.to_str
|
332
|
+
filename = filename.to_str
|
333
|
+
lineno = lineno.to_int
|
334
|
+
begin
|
335
|
+
Kernel.eval(code,binding,filename,lineno)
|
336
|
+
rescue Exception => err
|
337
|
+
adjust_exception_backtrace err, code, filename, lineno
|
338
|
+
raise err
|
339
|
+
end
|
340
|
+
end
|
341
|
+
private :eval
|
342
|
+
|
343
|
+
# :stopdoc:
|
344
|
+
|
345
|
+
def adjust_exception_backtrace err, code, filename, lineno
|
346
|
+
rdx_frame = "#{filename}:#{lineno}"
|
347
|
+
if err.is_a?(SyntaxError) && md = err.message.match(/:(\d+):\s+/)
|
348
|
+
# The location we want is in the message, not in the backtrace
|
349
|
+
file,line = md.pre_match,md[1].to_i
|
350
|
+
if file==filename && (lineno..lineno+code.chomp("\n").count("\n")).cover?(line)
|
351
|
+
err.message.slice! 0...md.end(0)
|
352
|
+
err.message.slice! /^#{Regexp.escape(file)}:\d.+/m # The first syntax error is enough in the backtrace
|
353
|
+
unless err.backtrace.first==rdx_frame
|
354
|
+
err.set_backtrace err.backtrace.dup if err.backtrace.frozen?
|
355
|
+
err.backtrace.unshift(rdx_frame)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
# The following deals with artificial backtrace given (or modified) by the user
|
360
|
+
unless err.backtrace.find{ |line| line.start_with?(rdx_frame) }
|
361
|
+
# The information about the exact line number of the error is lost,
|
362
|
+
# but we don't want to lose completely the location of the runnable.
|
363
|
+
err.backtrace.unshift "#{rdx_frame}:in `RDX:indicative_location'"
|
364
|
+
end
|
365
|
+
unless err.backtrace.find{ |line| line.start_with?(__FILE__) }
|
366
|
+
# Will be removed by the backtrace filter if debug flag is off
|
367
|
+
err.backtrace.concat(caller)
|
368
|
+
end
|
369
|
+
err.backtrace.freeze # No more modification
|
370
|
+
end
|
371
|
+
private :adjust_exception_backtrace
|
372
|
+
|
373
|
+
protected
|
374
|
+
|
375
|
+
def register reportable
|
376
|
+
if [nil,self].include? starter
|
377
|
+
super
|
378
|
+
else
|
379
|
+
starter.register reportable
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def register_error err
|
384
|
+
return super unless starter
|
385
|
+
starter.instance_eval{ super(err,false) }
|
386
|
+
end
|
387
|
+
|
388
|
+
def register_failure fail, conv_name, frame
|
389
|
+
register Failure.new(fail,conv_name,frame)
|
390
|
+
end
|
391
|
+
|
392
|
+
def register_success conv_name, frame
|
393
|
+
register Success.new(conv_name,frame)
|
394
|
+
end
|
395
|
+
|
396
|
+
def remove_magic_comment
|
397
|
+
md = text.match Text::REGEXP::MAGIC_COMMENT
|
398
|
+
return unless md
|
399
|
+
start,finish = md.offset(1)
|
400
|
+
text.slice! start...finish
|
401
|
+
end
|
402
|
+
private :remove_magic_comment
|
403
|
+
|
404
|
+
# :startdoc:
|
405
|
+
|
406
|
+
end
|
407
|
+
|
408
|
+
|
409
|
+
|
410
|
+
#
|
411
|
+
# A Runnable that RDX can start run.
|
412
|
+
#
|
413
|
+
class Runnable::Startable < Runnable
|
414
|
+
|
415
|
+
def initialize(*) # :nodoc:
|
416
|
+
super
|
417
|
+
self.binding = nil
|
418
|
+
@starter = nil
|
419
|
+
end
|
420
|
+
|
421
|
+
# The +binding+ used to #eval the code.
|
422
|
+
def binding
|
423
|
+
@binding || super
|
424
|
+
end
|
425
|
+
|
426
|
+
# The value of +self+ at the toplevel of #binding.
|
427
|
+
def toplevel_self
|
428
|
+
return super unless @binding
|
429
|
+
@toplevel_self ||= @binding.eval 'self'
|
430
|
+
end
|
431
|
+
|
432
|
+
# The <em>lexical scope</em> at the toplevel of #binding.
|
433
|
+
def toplevel_scope
|
434
|
+
return super unless @binding
|
435
|
+
@toplevel_scope ||= @binding.eval('::Module.nesting').first || ::Object
|
436
|
+
end
|
437
|
+
|
438
|
+
#
|
439
|
+
# :section: API
|
440
|
+
#
|
441
|
+
|
442
|
+
# Sets the #binding to the given _binding_.
|
443
|
+
def binding= binding
|
444
|
+
@binding = binding
|
445
|
+
clear_binding_info
|
446
|
+
end
|
447
|
+
|
448
|
+
# Skip the whole run and register a Skip in the report.
|
449
|
+
def whole_skip msg=nil, type=nil, frame=nil
|
450
|
+
skip_building
|
451
|
+
define_singleton_method(:whole_run){ |*| skip msg, type, frame }
|
452
|
+
end
|
453
|
+
|
454
|
+
module InTmpDir # :nodoc:
|
455
|
+
def whole_run(*)
|
456
|
+
prefix = File.basename(self.file_name,'.*') + "_#{self.line_no}-"
|
457
|
+
in_tmpdir(prefix,Dir.pwd){ super }
|
458
|
+
end
|
459
|
+
def normalize_values exp, act, &normalize
|
460
|
+
if exp.is_a?(String) && act.is_a?(String)
|
461
|
+
exp = Text.normalize_path(exp)
|
462
|
+
act = Text.normalize_path(act)
|
463
|
+
end
|
464
|
+
super
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
#
|
469
|
+
# Do the whole run with the current directory set to a new temporary directory
|
470
|
+
# (actually the whole RDX process is run in a common temporary directory;
|
471
|
+
# this makes an empty subdirectory and moves into).
|
472
|
+
# #normalize_values is enhanced so that it calls Text#normalize_path
|
473
|
+
# for directly compared strings.
|
474
|
+
#
|
475
|
+
# When creating files or directories in the example, this directive deletes all
|
476
|
+
# of them for you, avoiding explicit calls to <tt>FileUtils.rm<tt> and friends.
|
477
|
+
#
|
478
|
+
def run_in_tmpdir
|
479
|
+
extend InTmpDir
|
480
|
+
end
|
481
|
+
|
482
|
+
# :stopdoc:
|
483
|
+
|
484
|
+
def initialize_started
|
485
|
+
prepare_binding
|
486
|
+
end
|
487
|
+
private :initialize_started
|
488
|
+
|
489
|
+
def prepare_for_testing
|
490
|
+
return false if children.empty?
|
491
|
+
extend Started
|
492
|
+
return true
|
493
|
+
end
|
494
|
+
|
495
|
+
# Clears cached values related to binding.
|
496
|
+
def clear_binding_info
|
497
|
+
@toplevel_self = nil
|
498
|
+
@toplevel_scope = nil
|
499
|
+
end
|
500
|
+
private :clear_binding_info
|
501
|
+
|
502
|
+
def starter
|
503
|
+
@starter || super
|
504
|
+
end
|
505
|
+
def started?
|
506
|
+
@starter
|
507
|
+
end
|
508
|
+
|
509
|
+
def whole_run(starter=nil,beginned=false)
|
510
|
+
if @starter=starter and !ancestors.include?(starter)
|
511
|
+
starter.add_caller_frame = true
|
512
|
+
end
|
513
|
+
begin
|
514
|
+
run_setup(self) if beginned
|
515
|
+
super()
|
516
|
+
ensure
|
517
|
+
run_teardown(self) if beginned
|
518
|
+
end
|
519
|
+
starter.add_caller_frame = false if starter
|
520
|
+
end
|
521
|
+
|
522
|
+
delegate_to_parent :run_setup
|
523
|
+
|
524
|
+
delegate_to_parent :run_teardown
|
525
|
+
|
526
|
+
def clear_run_hooks!
|
527
|
+
define_singleton_method(:run_setup){ |*| }
|
528
|
+
define_singleton_method(:run_teardown){ |*| }
|
529
|
+
end
|
530
|
+
|
531
|
+
def normalize_values exp, act
|
532
|
+
[exp, act]
|
533
|
+
end
|
534
|
+
|
535
|
+
# :startdoc:
|
536
|
+
|
537
|
+
#
|
538
|
+
# :section: Internal
|
539
|
+
#
|
540
|
+
|
541
|
+
# The default #binding for executing code is Binding.pseudo_toplevel.
|
542
|
+
# In this way the true toplevel environment is quite protected from contamination.
|
543
|
+
def prepare_binding
|
544
|
+
self.binding ||= Binding.pseudo_toplevel
|
545
|
+
end
|
546
|
+
|
547
|
+
end
|
548
|
+
|
549
|
+
|
550
|
+
|
551
|
+
module Runnable::Started # :nodoc:
|
552
|
+
|
553
|
+
attr_reader :reportables
|
554
|
+
|
555
|
+
attr_reader :before_require
|
556
|
+
|
557
|
+
attr_accessor :add_caller_frame
|
558
|
+
|
559
|
+
def self.extended startable
|
560
|
+
startable.instance_eval{ initialize_started }
|
561
|
+
end
|
562
|
+
|
563
|
+
def initialize_started
|
564
|
+
super
|
565
|
+
@reportables = []
|
566
|
+
@before_require ||= nil
|
567
|
+
@add_caller_frame = false
|
568
|
+
end
|
569
|
+
private :initialize_started
|
570
|
+
|
571
|
+
def passed?
|
572
|
+
reportables.none?(&:failure?)
|
573
|
+
end
|
574
|
+
|
575
|
+
def full_run
|
576
|
+
pwd = Dir.pwd
|
577
|
+
whole_run(self,true)
|
578
|
+
ensure
|
579
|
+
Dir.chdir(pwd) unless Dir.pwd==pwd
|
580
|
+
end
|
581
|
+
|
582
|
+
def result_code
|
583
|
+
result = reportables.max_by(&:code_priority) || Success
|
584
|
+
result.result_code
|
585
|
+
end
|
586
|
+
|
587
|
+
def register reportable
|
588
|
+
reportable.before_require = self.before_require
|
589
|
+
reportable.rdx_caller = self.to_s if add_caller_frame
|
590
|
+
reportables << reportable
|
591
|
+
super
|
592
|
+
end
|
593
|
+
|
594
|
+
end
|
595
|
+
|
596
|
+
end
|
597
|
+
|
598
|
+
end
|