Module: Scampi
- Defined in:
- lib/scampi.rb,
lib/scampi/error.rb,
lib/scampi/colors.rb,
lib/scampi/should.rb,
lib/scampi/context.rb,
lib/scampi/version.rb
Overview
Minimal ANSI coloring — trimmed from the colorize gem to only the two colors Scampi actually uses (green for "ok", red for "not ok"). Output is byte-for-byte identical to colorize's String#green / String#red.
Defined Under Namespace
Modules: Colors Classes: Context, Error, Should
Constant Summary collapse
- Counter =
Global counters tracking specifications, requirements, failures, errors, nesting depth, and whether the summary hook has been installed.
Hash.new(0)
- ErrorLog =
Mutable string that accumulates error backtraces for TAP diagnostic output.
"".dup
Hash.new { |_, name| raise NameError, "no such context: #{name.inspect}" }
- RestrictName =
Regex filter for spec names. Only specs matching this pattern will run.
//- RestrictContext =
Regex filter for context names. Only contexts matching this pattern will run.
//- Backtraces =
Whether to include backtraces in TAP diagnostic output on failure.
true- VERSION =
"1.0.0"
Class Method Summary collapse
-
.handle_requirement(description, indent = 0, local_n = 1) ⇒ Object
Execute a single requirement block and emit the TAP ok/not-ok line.
-
.load_test_file(file) ⇒ Object
Load a test file, queuing whatever specs it defines.
-
.queue ⇒ Object
The global queue of test items (contexts and raw specs).
-
.run ⇒ Object
Run all queued tests and emit TAP version 14 output.
-
.run_bare_spec(description, block, n) ⇒ Object
Run a single spec that lives outside any describe block.
-
.summary_on_exit ⇒ Object
(also: summary_at_exit)
Install an
at_exithook that runs all queued tests and sets the exit code to 1 if there were any failures or errors.
Class Method Details
.handle_requirement(description, indent = 0, local_n = 1) ⇒ Object
Execute a single requirement block and emit the TAP ok/not-ok line.
The block should return an empty string on success, or an error description string on failure.
140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/scampi.rb', line 140 def self.handle_requirement(description, indent = 0, local_n = 1) ErrorLog.replace "" error = yield prefix = " " * indent if error.empty? puts "#{prefix}#{"ok".green} #{local_n} - #{description}" true else puts "#{prefix}#{"not ok".red} #{local_n} - #{description}: #{error}" puts ErrorLog.strip.gsub(/^/, "#{prefix}# ") if Backtraces false end end |
.load_test_file(file) ⇒ Object
Load a test file, queuing whatever specs it defines.
Two styles are supported:
-
Co-located
__END__tests -- the file's real code runs (Ruby stops parsing at__END__), then the section after__END__is evaluated as spec code. BecauseDATA/__END__is only populated for the directly-run script, we read and eval the tail ourselves, preserving the original file and line numbers for backtraces. -
Plain spec files -- files with
describe/itat the top level and no__END__are simply loaded.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/scampi.rb', line 97 def self.load_test_file(file) src = File.read(file) # Run the implementation code. Ruby ignores everything past __END__. old_verbose, $-w = $-w, nil load file $-w = old_verbose # If there's an __END__ section, eval its body as spec code. return unless src =~ /^__END__$/ head, tail = src.split(/^__END__$\n?/, 2) return if tail.nil? || tail.strip.empty? lineno = head.count("\n") + 2 # first line after the __END__ marker eval(tail, TOPLEVEL_BINDING, file, lineno) end |
.queue ⇒ Object
The global queue of test items (contexts and raw specs).
44 45 46 |
# File 'lib/scampi.rb', line 44 def self.queue @queue end |
.run ⇒ Object
Run all queued tests and emit TAP version 14 output.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/scampi.rb', line 49 def self.run return if @ran @ran = true # Register: evaluate all describe blocks to discover specs @queue.each { |item| item.register if item.is_a?(Context) } # TAP version + plan puts "TAP version 14" puts "1..#{@queue.size}" # Execute: contexts become subtests, raw specs become flat lines @queue.each_with_index do |item, i| n = i + 1 if item.is_a?(Context) passed = item.execute(0) if passed puts "#{"ok".green} #{n} - #{item.name}" else puts "#{"not ok".red} #{n} - #{item.name}" end else _, description, block = item Counter[:specifications] += 1 passed = (description, block, n) end end # Summary comment tests, assertions, failures, errors = Counter.values_at(:specifications, :requirements, :failed, :errors) puts "# #{tests} tests, #{assertions} assertions, #{failures} failures, #{errors} errors" end |
.run_bare_spec(description, block, n) ⇒ Object
Run a single spec that lives outside any describe block.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/scampi.rb', line 160 def self.(description, block, n) handle_requirement(description, 0, n) do begin Counter[:depth] += 1 rescued = false begin prev_req = Counter[:requirements] block.call rescue Object => e rescued = true raise e ensure if Counter[:requirements] == prev_req and not rescued raise Error.new(:missing, "empty specification: #{description}") end end rescue SystemExit, Interrupt raise rescue Object => e ErrorLog << "#{e.class}: #{e.}\n" e.backtrace.find_all { |line| line !~ /bin\/scampi|\/scampi\.rb:\d+/ }. each_with_index { |line, i| ErrorLog << "\t#{line}#{i==0 ? ": #{description}" : ""}\n" } ErrorLog << "\n" if e.kind_of? Error Counter[e.count_as] += 1 e.count_as.to_s.upcase else Counter[:errors] += 1 "ERROR: #{e.class}" end else "" ensure Counter[:depth] -= 1 end end end |
.summary_on_exit ⇒ Object Also known as: summary_at_exit
Install an at_exit hook that runs all queued tests and sets the
exit code to 1 if there were any failures or errors.
116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/scampi.rb', line 116 def self.summary_on_exit return if Counter[:installed_summary] > 0 @timer = Time.now at_exit { run if $! raise $! elsif Counter[:errors] + Counter[:failed] > 0 exit 1 end } Counter[:installed_summary] += 1 end |