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.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.rdx +20 -0
  3. data/README +19 -0
  4. data/bin/rdx +7 -0
  5. data/examples/minimal/.rdx +8 -0
  6. data/examples/minimal/README +10 -0
  7. data/examples/minimal/lib/other_conventions.rb +64 -0
  8. data/examples/minimal/lib/the_basics.rb +94 -0
  9. data/examples/minimal/lib/using_directives.rb +66 -0
  10. data/examples/minimal/rakefile +27 -0
  11. data/examples/ruby-2.0.0-p0/README +7 -0
  12. data/examples/ruby-2.0.0-p0/install/core/.rdx +6 -0
  13. data/examples/ruby-2.0.0-p0/install/core/README +19 -0
  14. data/examples/ruby-2.0.0-p0/install/core/Rakefile +61 -0
  15. data/examples/ruby-2.0.0-p0/install/core/diffs/array.c.diff +166 -0
  16. data/examples/ruby-2.0.0-p0/install/core/diffs/bignum.c.diff +11 -0
  17. data/examples/ruby-2.0.0-p0/install/core/diffs/class.c.diff +36 -0
  18. data/examples/ruby-2.0.0-p0/install/core/diffs/compar.c.diff +11 -0
  19. data/examples/ruby-2.0.0-p0/install/core/diffs/complex.c.diff +301 -0
  20. data/examples/ruby-2.0.0-p0/install/core/diffs/cont.c.diff +65 -0
  21. data/examples/ruby-2.0.0-p0/install/core/diffs/dir.c.diff +147 -0
  22. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/re.rdoc.diff +328 -0
  23. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/security.rdoc.diff +8 -0
  24. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/standard_library.rdoc.diff +0 -0
  25. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax.rdoc.diff +0 -0
  26. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/assignment.rdoc.diff +160 -0
  27. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/calling_methods.rdoc.diff +130 -0
  28. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/control_expressions.rdoc.diff +254 -0
  29. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/exceptions.rdoc.diff +0 -0
  30. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/literals.rdoc.diff +54 -0
  31. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/methods.rdoc.diff +157 -0
  32. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/miscellaneous.rdoc.diff +91 -0
  33. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/modules_and_classes.rdoc.diff +161 -0
  34. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/precedence.rdoc.diff +8 -0
  35. data/examples/ruby-2.0.0-p0/install/core/diffs/doc/syntax/refinements.rdoc.diff +146 -0
  36. data/examples/ruby-2.0.0-p0/install/core/diffs/encoding.c.diff +276 -0
  37. data/examples/ruby-2.0.0-p0/install/core/diffs/enum.c.diff +281 -0
  38. data/examples/ruby-2.0.0-p0/install/core/diffs/enumerator.c.diff +479 -0
  39. data/examples/ruby-2.0.0-p0/install/core/diffs/error.c.diff +143 -0
  40. data/examples/ruby-2.0.0-p0/install/core/diffs/eval.c.diff +47 -0
  41. data/examples/ruby-2.0.0-p0/install/core/diffs/eval_jump.c.diff +23 -0
  42. data/examples/ruby-2.0.0-p0/install/core/diffs/file.c.diff +752 -0
  43. data/examples/ruby-2.0.0-p0/install/core/diffs/gc.c.diff +195 -0
  44. data/examples/ruby-2.0.0-p0/install/core/diffs/hash.c.diff +84 -0
  45. data/examples/ruby-2.0.0-p0/install/core/diffs/iseq.c.diff +354 -0
  46. data/examples/ruby-2.0.0-p0/install/core/diffs/load.c.diff +53 -0
  47. data/examples/ruby-2.0.0-p0/install/core/diffs/marshal.c.diff +98 -0
  48. data/examples/ruby-2.0.0-p0/install/core/diffs/math.c.diff +110 -0
  49. data/examples/ruby-2.0.0-p0/install/core/diffs/numeric.c.diff +103 -0
  50. data/examples/ruby-2.0.0-p0/install/core/diffs/object.c.diff +295 -0
  51. data/examples/ruby-2.0.0-p0/install/core/diffs/pack.c.diff +18 -0
  52. data/examples/ruby-2.0.0-p0/install/core/diffs/parse.y.diff +23 -0
  53. data/examples/ruby-2.0.0-p0/install/core/diffs/proc.c.diff +155 -0
  54. data/examples/ruby-2.0.0-p0/install/core/diffs/random.c.diff +126 -0
  55. data/examples/ruby-2.0.0-p0/install/core/diffs/range.c.diff +49 -0
  56. data/examples/ruby-2.0.0-p0/install/core/diffs/rational.c.diff +312 -0
  57. data/examples/ruby-2.0.0-p0/install/core/diffs/re.c.diff +207 -0
  58. data/examples/ruby-2.0.0-p0/install/core/diffs/ruby.c.diff +21 -0
  59. data/examples/ruby-2.0.0-p0/install/core/diffs/signal.c.diff +67 -0
  60. data/examples/ruby-2.0.0-p0/install/core/diffs/sprintf.c.diff +29 -0
  61. data/examples/ruby-2.0.0-p0/install/core/diffs/string.c.diff +73 -0
  62. data/examples/ruby-2.0.0-p0/install/core/diffs/struct.c.diff +20 -0
  63. data/examples/ruby-2.0.0-p0/install/core/diffs/time.c.diff +691 -0
  64. data/examples/ruby-2.0.0-p0/install/core/diffs/transcode.c.diff +435 -0
  65. data/examples/ruby-2.0.0-p0/install/core/diffs/variable.c.diff +62 -0
  66. data/examples/ruby-2.0.0-p0/install/core/diffs/vm_backtrace.c.diff +164 -0
  67. data/examples/ruby-2.0.0-p0/install/core/diffs/vm_eval.c.diff +99 -0
  68. data/examples/ruby-2.0.0-p0/install/core/diffs/vm_method.c.diff +17 -0
  69. data/examples/ruby-2.0.0-p0/install/core/diffs/vm_trace.c.diff +393 -0
  70. data/examples/ruby-2.0.0-p0/install/stdlib/.rdx +6 -0
  71. data/examples/ruby-2.0.0-p0/install/stdlib/README +19 -0
  72. data/examples/ruby-2.0.0-p0/install/stdlib/Rakefile +53 -0
  73. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/abbrev.rb.diff +77 -0
  74. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/base64.rb.diff +42 -0
  75. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/benchmark.rb.diff +144 -0
  76. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/cmath.rb.diff +52 -0
  77. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/forwardable.rb.diff +150 -0
  78. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/mathn.rb.diff +58 -0
  79. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/matrix.rb.diff +657 -0
  80. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/observer.rb.diff +31 -0
  81. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/optparse.rb.diff +147 -0
  82. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/ostruct.rb.diff +78 -0
  83. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/prime.rb.diff +52 -0
  84. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/pstore.rb.diff +110 -0
  85. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/scanf.rb.diff +100 -0
  86. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/securerandom.rb.diff +144 -0
  87. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/set.rb.diff +637 -0
  88. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/shellwords.rb.diff +66 -0
  89. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/singleton.rb.diff +37 -0
  90. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/tempfile.rb.diff +104 -0
  91. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/thread.rb.diff +38 -0
  92. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/time.rb.diff +140 -0
  93. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/tmpdir.rb.diff +52 -0
  94. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/uri.rb.diff +39 -0
  95. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/uri/common.rb.diff +237 -0
  96. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/weakref.rb.diff +36 -0
  97. data/examples/ruby-2.0.0-p0/install/stdlib/diffs/lib/yaml/store.rb.diff +27 -0
  98. data/examples/ruby-2.0.0-p0/rakefile +165 -0
  99. data/lib/rdx.rb +331 -0
  100. data/lib/rdx/assertions.rb +484 -0
  101. data/lib/rdx/binding.rb +151 -0
  102. data/lib/rdx/code_object.rb +598 -0
  103. data/lib/rdx/comment.rb +338 -0
  104. data/lib/rdx/convention.rb +1174 -0
  105. data/lib/rdx/directive.rb +1432 -0
  106. data/lib/rdx/example.rb +679 -0
  107. data/lib/rdx/generator.rb +112 -0
  108. data/lib/rdx/generator/rdoc.rb +1006 -0
  109. data/lib/rdx/options.rb +359 -0
  110. data/lib/rdx/plain_text.rb +65 -0
  111. data/lib/rdx/reporter.rb +421 -0
  112. data/lib/rdx/ruby_lex.rb +324 -0
  113. data/lib/rdx/runner.rb +309 -0
  114. data/lib/rdx/source_file.rb +94 -0
  115. data/lib/rdx/specification.rb +194 -0
  116. data/lib/rdx/statement.rb +248 -0
  117. data/lib/rdx/store.rb +119 -0
  118. data/lib/rdx/task.rb +361 -0
  119. data/lib/rdx/text.rb +688 -0
  120. data/lib/rdx/version.rb +15 -0
  121. data/rakefile +64 -0
  122. metadata +203 -0
@@ -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