Module: Tokyo
- Extended by:
- Tokyo
- Included in:
- Tokyo
- Defined in:
- lib/tokyo/pretty_print.rb,
lib/tokyo.rb,
lib/tokyo/run.rb,
lib/tokyo/unit.rb,
lib/tokyo/util.rb,
lib/tokyo/assert.rb,
lib/tokyo/expectations.rb,
lib/tokyo/expectations/with.rb,
lib/tokyo/util/assert_raise.rb,
lib/tokyo/util/assert_throw.rb,
lib/tokyo/util/refute_raise.rb,
lib/tokyo/util/refute_throw.rb,
lib/tokyo/expectations/raise.rb,
lib/tokyo/expectations/throw.rb,
lib/tokyo/expectations/return.rb
Overview
Stolen from [Pry](github.com/pry/pry)
Copyright © 2013 John Mair (banisterfiend) Copyright © 2015 Slee Woo (sleewoo)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Defined Under Namespace
Classes: Assert, AssertionFailure, Expectation, GenericFailure, PrettyPrint, Skip, Unit
Constant Summary collapse
- DEFAULT_PATTERN =
'{spec,test}/**/{*_spec.rb,*_test.rb}'.freeze
- GLOBAL_SETUPS =
[]
- INDENT =
' '.freeze
- PASTEL =
Pastel.new
Instance Method Summary collapse
- #assert_expected_symbol_thrown(object, expected_symbol) ⇒ Object
- #assert_raised(object) ⇒ Object
- #assert_raised_as_expected(object, expected_type = nil, expected_message = nil, block = nil) ⇒ Object
- #assert_raised_as_expected_by_block(object, block) ⇒ Object
- #assert_raised_expected_message(object, expected_message) ⇒ Object
- #assert_raised_expected_type(object, expected_type) ⇒ Object
- #assert_thrown(object) ⇒ Object
- #assert_thrown_as_expected(object, expected_symbol = nil, block = nil) ⇒ Object
- #assert_thrown_as_expected_by_block(object, block) ⇒ Object
- #assertions ⇒ Object
- #augment_load_path(file) ⇒ Object
- #call_block(block) ⇒ Object
- #caller_to_source_location(caller) ⇒ Object
- #define_and_register_a_context(label, block, parent) ⇒ Object
- #define_and_register_a_spec(label, block) ⇒ Object
- #define_context(label, block, parent) ⇒ Object
- #define_spec(label, block) ⇒ Object
-
#define_unit_class(type, label, block, ancestors) ⇒ Unit
define a class that will hold contexts and tests.
-
#define_unit_module(block) ⇒ Module
define a module that when included will execute the given block on base.
-
#extract_thrown_symbol(exception) ⇒ Object
extract thrown symbol from given exception.
-
#fail(reason, caller) ⇒ Object
stop any code and report a failure.
- #find_files(pattern_or_files) ⇒ Object
- #identity_string(type, label, block) ⇒ Object
- #load_file(file) ⇒ Object
- #pp(obj) ⇒ Object
- #pretty_backtrace(e) ⇒ Object
- #progress ⇒ Object
- #pwd(*args) ⇒ Object
- #readline(caller) ⇒ Object
- #refute_expected_symbol_thrown(object, expected_symbol) ⇒ Object
- #refute_raised(object, should_raise = false) ⇒ Object
- #refute_raised_as_expected(object, expected_type, expected_message, block = nil) ⇒ Object
- #refute_raised_expected_message(object, expected_message) ⇒ Object
- #refute_raised_expected_type(object, expected_type) ⇒ Object
- #refute_thrown(object, should_throw = false) ⇒ Object
- #refute_thrown_as_expected(object, expected_symbol, block = nil) ⇒ Object
- #relative_location(line) ⇒ Object
- #relative_source_location(block) ⇒ Object
- #render_AssertionFailure(indent, failure) ⇒ Object
- #render_caller(indent, caller) ⇒ Object
- #render_exception(indent, failure) ⇒ Object
- #render_failure(unit, test_uuid, failure) ⇒ Object
- #render_GenericFailure(indent, failure) ⇒ Object
- #render_skips ⇒ Object
- #render_totals(specs, tests, assertions) ⇒ Object
- #run(pattern_or_files = DEFAULT_PATTERN) ⇒ Object
- #skips ⇒ Object
- #total_assertions ⇒ Object
- #totals ⇒ Object
- #units ⇒ Object
Instance Method Details
#assert_expected_symbol_thrown(object, expected_symbol) ⇒ Object
31 32 33 34 35 36 37 38 39 |
# File 'lib/tokyo/util/assert_throw.rb', line 31 def assert_expected_symbol_thrown object, expected_symbol return begin [ 'Expected :%s to be thrown at %s' % [expected_symbol, object[:caller]], 'Instead :%s thrown' % object[:thrown] ] end unless expected_symbol == object[:thrown] nil end |
#assert_raised(object) ⇒ Object
21 22 23 24 25 26 |
# File 'lib/tokyo/util/assert_raise.rb', line 21 def assert_raised object return [ 'Expected a exception to be raised at %s' % object[:caller] ] unless object[:raised] nil end |
#assert_raised_as_expected(object, expected_type = nil, expected_message = nil, block = nil) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# File 'lib/tokyo/util/assert_raise.rb', line 3 def assert_raised_as_expected object, expected_type = nil, = nil, block = nil f = assert_raised(object) return f if f return assert_raised_as_expected_by_block(object, block) if block if expected_type f = assert_raised_expected_type(object, expected_type) return f if f end if f = (object, ) return f if f end nil end |
#assert_raised_as_expected_by_block(object, block) ⇒ Object
28 29 30 31 32 33 34 |
# File 'lib/tokyo/util/assert_raise.rb', line 28 def assert_raised_as_expected_by_block object, block return [ 'Looks like wrong or no error raised at %s' % object[:caller], 'See validation block' ] unless block.call(object[:raised]) nil end |
#assert_raised_expected_message(object, expected_message) ⇒ Object
44 45 46 47 48 49 50 51 52 53 |
# File 'lib/tokyo/util/assert_raise.rb', line 44 def object, regexp = .is_a?(Regexp) ? : /\A#{}\z/ return [ 'Expected the exception raised at %s' % object[:caller], 'to match "%s"' % regexp.source, 'Instead it looks like', pp(object[:raised].) ] unless object[:raised]. =~ regexp nil end |
#assert_raised_expected_type(object, expected_type) ⇒ Object
36 37 38 39 40 41 42 |
# File 'lib/tokyo/util/assert_raise.rb', line 36 def assert_raised_expected_type object, expected_type return [ 'Expected a %s to be raised at %s' % [expected_type, object[:caller]], 'Instead a %s raised' % object[:raised].class ] unless object[:raised].class == expected_type nil end |
#assert_thrown(object) ⇒ Object
24 25 26 27 28 29 |
# File 'lib/tokyo/util/assert_throw.rb', line 24 def assert_thrown object return [ 'Expected a symbol to be thrown at %s' % object[:caller] ] unless object[:thrown] nil end |
#assert_thrown_as_expected(object, expected_symbol = nil, block = nil) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 |
# File 'lib/tokyo/util/assert_throw.rb', line 3 def assert_thrown_as_expected object, expected_symbol = nil, block = nil f = assert_thrown(object) return f if f return assert_thrown_as_expected_by_block(object, block) if block if expected_symbol f = assert_expected_symbol_thrown(object, expected_symbol) return f if f end nil end |
#assert_thrown_as_expected_by_block(object, block) ⇒ Object
16 17 18 19 20 21 22 |
# File 'lib/tokyo/util/assert_throw.rb', line 16 def assert_thrown_as_expected_by_block object, block return [ 'Looks like wrong or no symbol thrown at %s' % object[:caller], 'See validating block' ] unless block.call(object[:thrown]) nil end |
#assertions ⇒ Object
34 35 36 |
# File 'lib/tokyo.rb', line 34 def assertions @assertions ||= {} end |
#augment_load_path(file) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/tokyo/util.rb', line 68 def augment_load_path file # adding ./ $:.unshift(pwd) unless $:.include?(pwd) # adding ./lib/ lib = pwd('lib') unless $:.include?(lib) $:.unshift(lib) if File.directory?(lib) end # adding file's dirname dir = File.dirname(file) $:.unshift(dir) unless $:.include?(dir) end |
#call_block(block) ⇒ Object
3 4 5 6 7 8 9 |
# File 'lib/tokyo/util.rb', line 3 def call_block block {returned: block.call, caller: relative_source_location(block)}.freeze rescue UncaughtThrowError => e {raised: e, thrown: extract_thrown_symbol(e), caller: relative_source_location(block)}.freeze rescue Exception => e {raised: e, caller: relative_source_location(block)}.freeze end |
#caller_to_source_location(caller) ⇒ Object
53 54 55 56 |
# File 'lib/tokyo/util.rb', line 53 def caller_to_source_location caller file, line = caller.split(/:(\d+):in.+/) [relative_location(file), line] end |
#define_and_register_a_context(label, block, parent) ⇒ Object
58 59 60 |
# File 'lib/tokyo.rb', line 58 def define_and_register_a_context label, block, parent units << define_context(label, block, parent) end |
#define_and_register_a_spec(label, block) ⇒ Object
50 51 52 |
# File 'lib/tokyo.rb', line 50 def define_and_register_a_spec label, block units << define_spec(label, block) end |
#define_context(label, block, parent) ⇒ Object
54 55 56 |
# File 'lib/tokyo.rb', line 54 def define_context label, block, parent define_unit_class(:context, label, block, [*parent.__ancestors__, parent].freeze) end |
#define_spec(label, block) ⇒ Object
46 47 48 |
# File 'lib/tokyo.rb', line 46 def define_spec label, block define_unit_class(:spec, label, block, [].freeze) end |
#define_unit_class(type, label, block, ancestors) ⇒ Unit
define a class that will hold contexts and tests
70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/tokyo.rb', line 70 def define_unit_class type, label, block, ancestors identity = identity_string(type, label, block).freeze Class.new ancestors.last || Unit do define_singleton_method(:__ancestors__) {ancestors} define_singleton_method(:__identity__) {identity} Tokyo::GLOBAL_SETUPS.each {|b| class_exec(&b)} # execute given block only after global setups executed and all utility methods defined result = catch(:__tokyo_skip__) {class_exec(&block)} Tokyo.skips << result if result.is_a?(Skip) end end |
#define_unit_module(block) ⇒ Module
define a module that when included will execute the given block on base
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/tokyo.rb', line 87 def define_unit_module block block || raise(ArgumentError, 'missing block') Module.new do # any spec/context that will include this module will "inherit" it's logic # # @example # EnumeratorSpec = spec 'Enumerator tests' do # # some tests here # end # # spec Array do # include EnumeratorSpec # end # # spec Hash do # include EnumeratorSpec # end # define_singleton_method(:included) {|b| b.class_exec(&block)} end end |
#extract_thrown_symbol(exception) ⇒ Object
extract thrown symbol from given exception
15 16 17 18 19 |
# File 'lib/tokyo/util.rb', line 15 def extract_thrown_symbol exception return unless exception.is_a?(Exception) return unless s = exception..scan(/uncaught throw\W+(\w+)/).flatten[0] s.to_sym end |
#fail(reason, caller) ⇒ Object
stop any code and report a failure
110 111 112 |
# File 'lib/tokyo.rb', line 110 def fail reason, caller throw(:__tokyo_status__, GenericFailure.new(Array(reason), caller)) end |
#find_files(pattern_or_files) ⇒ Object
58 59 60 61 |
# File 'lib/tokyo/util.rb', line 58 def find_files pattern_or_files return pattern_or_files if pattern_or_files.is_a?(Array) Dir[pwd(pattern_or_files)] end |
#identity_string(type, label, block) ⇒ Object
21 22 23 24 25 26 27 |
# File 'lib/tokyo/util.rb', line 21 def identity_string type, label, block '%s %s (%s:%s)' % [ blue(type), label.inspect, *relative_source_location(block) ] end |
#load_file(file) ⇒ Object
63 64 65 66 |
# File 'lib/tokyo/util.rb', line 63 def load_file file augment_load_path(file) require(file) end |
#pp(obj) ⇒ Object
29 30 31 32 33 34 35 |
# File 'lib/tokyo/pretty_print.rb', line 29 def pp obj out = '' q = Tokyo::PrettyPrint.new(out) q.guard_inspect_key { q.pp(obj) } q.flush out end |
#pretty_backtrace(e) ⇒ Object
41 42 43 |
# File 'lib/tokyo/util.rb', line 41 def pretty_backtrace e Array(e.backtrace).map {|l| relative_location(l)} end |
#progress ⇒ Object
3 4 5 6 7 8 9 |
# File 'lib/tokyo/run.rb', line 3 def progress @progress ||= TTY::ProgressBar.new ':current of :total [:bar]' do |cfg| cfg.total = units.map {|u| u.tests.size}.reduce(:+) || 0 cfg.width = TTY::Screen.width cfg.complete = '.' end end |
#pwd(*args) ⇒ Object
83 84 85 |
# File 'lib/tokyo/util.rb', line 83 def pwd *args File.join(Dir.pwd, *args.map!(&:to_s)) end |
#readline(caller) ⇒ Object
45 46 47 48 49 50 51 |
# File 'lib/tokyo/util.rb', line 45 def readline caller file, line = caller_to_source_location(caller) return unless file && line lines = ((@__readlinecache__ ||= {})[file] ||= File.readlines(file)) return unless line = lines[line.to_i - 1] line.sub(/(do|\{)\Z/, '').strip end |
#refute_expected_symbol_thrown(object, expected_symbol) ⇒ Object
27 28 29 30 31 32 33 |
# File 'lib/tokyo/util/refute_throw.rb', line 27 def refute_expected_symbol_thrown object, expected_symbol return [ 'Not expected :%s to be thrown' % expected_symbol, 'at %s' % object[:caller] ] if expected_symbol == object[:thrown] nil end |
#refute_raised(object, should_raise = false) ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/tokyo/util/refute_raise.rb', line 19 def refute_raised object, should_raise = false if should_raise return [ 'Expected a exception to be raised at %s' % object[:caller] ] unless object[:raised] else return [ 'A unexpected exception raised at %s' % object[:caller], object[:raised] ] if object[:raised] end nil end |
#refute_raised_as_expected(object, expected_type, expected_message, block = nil) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/tokyo/util/refute_raise.rb', line 3 def refute_raised_as_expected object, expected_type, , block = nil f = refute_raised(object, expected_type || ) return f if f if expected_type f = refute_raised_expected_type(object, expected_type) return f if f end if f = (object, ) return f if f end nil end |
#refute_raised_expected_message(object, expected_message) ⇒ Object
40 41 42 43 44 45 46 47 |
# File 'lib/tokyo/util/refute_raise.rb', line 40 def object, regexp = .is_a?(Regexp) ? : /\A#{}\z/ return [ 'Not expected raised exception to match %s' % regexp.source, object[:raised] ] if object[:raised]. =~ regexp nil end |
#refute_raised_expected_type(object, expected_type) ⇒ Object
33 34 35 36 37 38 |
# File 'lib/tokyo/util/refute_raise.rb', line 33 def refute_raised_expected_type object, expected_type return [ 'Not expected a %s to be raised at %s' % [object[:raised].class, object[:caller]], ] if object[:raised].class == expected_type nil end |
#refute_thrown(object, should_throw = false) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/tokyo/util/refute_throw.rb', line 14 def refute_thrown object, should_throw = false if should_throw return [ 'Expected a symbol to be thrown at %s' % object[:caller] ] unless object[:thrown] else return [ 'Not expected a symbol to be thrown at %s' % object[:caller] ] if object[:thrown] end nil end |
#refute_thrown_as_expected(object, expected_symbol, block = nil) ⇒ Object
3 4 5 6 7 8 9 10 11 12 |
# File 'lib/tokyo/util/refute_throw.rb', line 3 def refute_thrown_as_expected object, expected_symbol, block = nil f = refute_thrown(object, expected_symbol) return f if f if expected_symbol f = refute_expected_symbol_thrown(object, expected_symbol) return f if f end nil end |
#relative_location(line) ⇒ Object
37 38 39 |
# File 'lib/tokyo/util.rb', line 37 def relative_location line line.sub(/\A#{pwd}\/+/, '') end |
#relative_source_location(block) ⇒ Object
29 30 31 32 33 34 35 |
# File 'lib/tokyo/util.rb', line 29 def relative_source_location block return unless block [ relative_location(block.source_location[0]), block.source_location[1] ] end |
#render_AssertionFailure(indent, failure) ⇒ Object
107 108 109 110 |
# File 'lib/tokyo/run.rb', line 107 def render_AssertionFailure indent, failure progress.log indent + cyan('a: ') + pp(failure.object) progress.log indent + cyan('b: ') + failure.arguments.map {|a| pp(a)}.join(', ') end |
#render_caller(indent, caller) ⇒ Object
112 113 114 115 |
# File 'lib/tokyo/run.rb', line 112 def render_caller indent, caller return unless caller progress.log indent + underline.bright_red(readline(caller)) end |
#render_exception(indent, failure) ⇒ Object
98 99 100 101 |
# File 'lib/tokyo/run.rb', line 98 def render_exception indent, failure progress.log indent + underline.bright_red([failure.class, failure.]*': ') pretty_backtrace(failure).each {|l| progress.log(indent + l)} end |
#render_failure(unit, test_uuid, failure) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/tokyo/run.rb', line 78 def render_failure unit, test_uuid, failure indent = '' [*unit.__ancestors__, unit].each do |u| progress.log indent + u.__identity__ indent << INDENT end progress.log indent + test_uuid indent << INDENT case failure when Exception render_exception(indent, failure) when GenericFailure, AssertionFailure render_caller(indent, failure.caller) __send__('render_%s' % failure.class.name.split('::').last, indent, failure) else progress.log(indent + failure.inspect) end progress.log '' end |
#render_GenericFailure(indent, failure) ⇒ Object
103 104 105 |
# File 'lib/tokyo/run.rb', line 103 def render_GenericFailure indent, failure Array(failure.reason).each {|l| progress.log(indent + l.to_s)} end |
#render_skips ⇒ Object
117 118 119 120 121 122 123 124 125 |
# File 'lib/tokyo/run.rb', line 117 def render_skips return if skips.empty? puts puts bold.magenta('Skips:') skips.each do |skip| puts ' %s (%s)' % [blue(skip['reason'] || 'skip'), relative_location(skip['caller'])] end puts end |
#render_totals(specs, tests, assertions) ⇒ Object
69 70 71 72 73 74 75 76 |
# File 'lib/tokyo/run.rb', line 69 def render_totals specs, tests, assertions puts puts puts bold.cyan(' Specs: %i' % specs) puts bold.cyan(' Tests: %i' % tests) puts bold.cyan(' Assertions: %i' % assertions) puts end |
#run(pattern_or_files = DEFAULT_PATTERN) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/tokyo/run.rb', line 11 def run pattern_or_files = DEFAULT_PATTERN specs = 0 tests = 0 assertions = 0 find_files(pattern_or_files).shuffle.each do |file| r, w = IO.pipe pid = Kernel.fork do r.close load_file(file) progress.log '' progress.log cyan(relative_location(file)) units.shuffle.each do |unit| # exceptions raised inside unit#__run__ will be treated as failures and pretty printed. # any other exceptions will be treated as implementation errors and ugly printed. unit.tests.keys.shuffle.each do |test| status = unit.run(test) if status.is_a?(Skip) w.puts({skip: true, reason: status.reason, caller: status.caller}.to_json) else unless status == :__tokyo_passed__ render_failure(unit, unit.tests[test], status) Kernel.exit(1) end end progress.advance end end w.puts(totals.to_json) end _, status = Process.waitpid2(pid) Kernel.exit(1) unless status.success? w.close while line = r.gets line = JSON.parse(line) if line['skip'] skips << line elsif line['totals'] specs += line['specs'] tests += line['tests'] assertions += line['assertions'] else raise('Incomprehensible message received: %s' % line) end end end render_skips render_totals(specs, tests, assertions) end |
#skips ⇒ Object
42 43 44 |
# File 'lib/tokyo.rb', line 42 def skips @skips ||= [] end |
#total_assertions ⇒ Object
38 39 40 |
# File 'lib/tokyo.rb', line 38 def total_assertions @total_assertions ||= [] end |
#totals ⇒ Object
60 61 62 63 64 65 66 67 |
# File 'lib/tokyo/run.rb', line 60 def totals { totals: true, specs: units.select {|u| u.__ancestors__.empty?}.size, tests: units.map {|u| u.tests.size}.reduce(:+), assertions: total_assertions.size } end |
#units ⇒ Object
30 31 32 |
# File 'lib/tokyo.rb', line 30 def units @units ||= [] end |