speckle 0.1.2

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 (67) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +39 -0
  6. data/Rakefile +230 -0
  7. data/bin/speckle +12 -0
  8. data/lib/dsl.riml +29 -0
  9. data/lib/expectation.riml +34 -0
  10. data/lib/matchers/above_matcher.riml +13 -0
  11. data/lib/matchers/atleast_matcher.riml +13 -0
  12. data/lib/matchers/atmost_matcher.riml +13 -0
  13. data/lib/matchers/below_matcher.riml +13 -0
  14. data/lib/matchers/between_matcher.riml +19 -0
  15. data/lib/matchers/boolean_matcher.riml +13 -0
  16. data/lib/matchers/dict_key_matcher.riml +14 -0
  17. data/lib/matchers/equality_matcher.riml +13 -0
  18. data/lib/matchers/existance_matcher.riml +13 -0
  19. data/lib/matchers/length_matcher.riml +14 -0
  20. data/lib/matchers/match_item.riml +8 -0
  21. data/lib/matchers/match_tester.riml +33 -0
  22. data/lib/matchers/matchers.riml +72 -0
  23. data/lib/matchers/regexp_matcher.riml +14 -0
  24. data/lib/matchers/within_matcher.riml +28 -0
  25. data/lib/reporters/base_reporter.riml +126 -0
  26. data/lib/reporters/dotmatrix_reporter.riml +47 -0
  27. data/lib/reporters/min_reporter.riml +13 -0
  28. data/lib/reporters/reporter_factory.riml +15 -0
  29. data/lib/reporters/spec_reporter.riml +58 -0
  30. data/lib/reporters/tap_reporter.riml +38 -0
  31. data/lib/runners/runner.riml +49 -0
  32. data/lib/runners/spec_runner.riml +96 -0
  33. data/lib/speckle/cli/app.rb +18 -0
  34. data/lib/speckle/cli/controller.rb +67 -0
  35. data/lib/speckle/cli/environment.rb +148 -0
  36. data/lib/speckle/cli/rake_app.rb +146 -0
  37. data/lib/speckle/cli/router.rb +16 -0
  38. data/lib/speckle/loader.rb +10 -0
  39. data/lib/speckle/version.rb +3 -0
  40. data/lib/speckle.rb +4 -0
  41. data/lib/speckle.riml +148 -0
  42. data/lib/utils/spec_meta.riml +30 -0
  43. data/lib/utils/spec_timer.riml +30 -0
  44. data/lib/utils/statistician.riml +70 -0
  45. data/lib/writers/buffer_writer.riml +37 -0
  46. data/lib/writers/console_writer.riml +28 -0
  47. data/lib/writers/file_writer.riml +31 -0
  48. data/lib/writers/writer_factory.riml +13 -0
  49. data/spec/after_hooks_spec.riml +58 -0
  50. data/spec/before_hooks_spec.riml +38 -0
  51. data/spec/matchers/above_matcher_spec.riml +27 -0
  52. data/spec/matchers/atleast_matcher_spec.riml +28 -0
  53. data/spec/matchers/atmost_matcher_spec.riml +29 -0
  54. data/spec/matchers/below_matcher_spec.riml +28 -0
  55. data/spec/matchers/between_matcher_spec.riml +17 -0
  56. data/spec/matchers/boolean_matcher_spec.riml +27 -0
  57. data/spec/matchers/custom_matcher_spec.riml +47 -0
  58. data/spec/matchers/dict_key_matcher_spec.riml +19 -0
  59. data/spec/matchers/equality_matcher_spec.riml +31 -0
  60. data/spec/matchers/existance_matcher_spec.riml +17 -0
  61. data/spec/matchers/length_matcher_spec.riml +18 -0
  62. data/spec/matchers/regexp_matcher_spec.riml +31 -0
  63. data/spec/matchers/within_matcher_spec.riml +18 -0
  64. data/spec/spec_helper.rb +1 -0
  65. data/spec/speckle/cli/environment_spec.rb +296 -0
  66. data/speckle.gemspec +30 -0
  67. metadata +210 -0
@@ -0,0 +1,14 @@
1
+ class RegExpMatcher
2
+ defm match(pattern, expr)
3
+ result = matchstr(expr, pattern)
4
+ return empty(result) == false
5
+ end
6
+
7
+ defm failure_message_for_match(pattern, expr)
8
+ return "expected “#{pattern}” to match #{expr}"
9
+ end
10
+
11
+ defm failure_message_for_mismatch(pattern, expr)
12
+ return "expected “#{pattern}” to not match #{expr}"
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ class WithinMatcher
2
+ defm match(expected, actual)
3
+ delta = expected[0]
4
+ num = expected[1]
5
+ self.result = abs(num - actual)
6
+ return self.result <= delta
7
+ end
8
+
9
+ defm failure_message_for_match(expected, actual)
10
+ delta = expected[0]
11
+ num = expected[1]
12
+ actual_str = printf("%f", actual)
13
+ delta_str = printf("%f", delta)
14
+ num_str = printf("%f", num)
15
+ result_str = printf('%f', self.result)
16
+ return "expected “#{actual_str}” to be within +/- “#{delta_str}” of “#{num_str}”, delta was “#{result_str}”"
17
+ end
18
+
19
+ defm failure_message_for_mismatch(expected, actual)
20
+ delta = expected[0]
21
+ num = expected[1]
22
+ actual_str = printf("%f", actual)
23
+ delta_str = printf("%f", delta)
24
+ num_str = printf("%f", num)
25
+ result_str = printf('%f', self.result)
26
+ return "expected “#{actual_str}” to not be within +/- “#{delta_str}” of “#{num_str}”, delta was “#{result_str}”"
27
+ end
28
+ end
@@ -0,0 +1,126 @@
1
+ class BaseReporter
2
+ def initialize()
3
+ self.colorize_output = true
4
+ end
5
+
6
+ defm set_writer(writer)
7
+ self.writer = writer
8
+ end
9
+
10
+ defm on_start(stats)
11
+ end
12
+
13
+ defm on_end(duration, stats)
14
+ self.write_epilogue(duration, stats)
15
+ end
16
+
17
+ defm on_context_start(context, stats)
18
+ end
19
+
20
+ defm on_context_end(context, stats)
21
+ end
22
+
23
+ defm on_spec_start(meta, stats)
24
+ end
25
+
26
+ defm on_spec_end(meta, stats)
27
+ end
28
+
29
+ defm on_spec_pass(meta, stats)
30
+ end
31
+
32
+ defm on_spec_failure(meta, err, stats)
33
+ end
34
+
35
+ defm on_spec_error(meta, err, stats)
36
+ end
37
+
38
+ defm on_spec_pending(meta, stats)
39
+ end
40
+
41
+ defm duration_to_str(duration)
42
+ time = a:duration
43
+ if time >= 1000
44
+ time = time / 1000
45
+ return "#{time}s"
46
+ else
47
+ return "#{time}ms"
48
+ end
49
+ end
50
+
51
+ defm get_duration_msg(meta)
52
+ if meta.is_slow()
53
+ duration_str = self.duration_to_str(meta.get_duration())
54
+ return "(#{duration_str})"
55
+ else
56
+ return ''
57
+ end
58
+ end
59
+
60
+ defm write_epilogue_separator
61
+ self.writer.writeln("----------------------------------------------------")
62
+ end
63
+
64
+ defm write_epilogue(duration, stats)
65
+ self.write_epilogue_separator()
66
+ duration_str = self.duration_to_str(duration)
67
+ if stats.is_ok()
68
+ icon = self.get_tick_icon()
69
+ else
70
+ icon = self.get_cross_icon()
71
+ end
72
+
73
+ self.writer.writeln(self.to_color("#{icon} #{stats.get_count()} tests completed (#{duration_str})", stats))
74
+ self.writer.writeln("Passed: #{stats.get_passes()}, Failures: #{stats.get_failures()}, Errors: #{stats.get_errors()}, Assertions: #{stats.get_assertions()}")
75
+ end
76
+
77
+ defm set_colorize_output(colorize_output)
78
+ self.colorize_output = colorize_output
79
+ end
80
+
81
+ defm get_colorize_output()
82
+ return self.colorize_output
83
+ end
84
+
85
+ defm colorize(str, color)
86
+ if self.get_colorize_output()
87
+ return "[#{color}#{str}"
88
+ else
89
+ return str
90
+ end
91
+ end
92
+
93
+ defm to_color(str, stats)
94
+ if stats.is_ok()
95
+ return self.to_green(str)
96
+ else
97
+ return self.to_red(str)
98
+ end
99
+ end
100
+
101
+ defm to_red(str)
102
+ return self.colorize(str, '31m')
103
+ end
104
+
105
+ defm to_green(str)
106
+ return self.colorize(str, '32m')
107
+ end
108
+
109
+ defm get_tick_icon()
110
+ return '✓'
111
+ end
112
+
113
+ defm get_cross_icon()
114
+ return '✖'
115
+ end
116
+
117
+ defm get_tick()
118
+ return self.to_green(self.get_tick_icon())
119
+ end
120
+
121
+ defm get_cross()
122
+ return self.to_red(self.get_cross_icon())
123
+ end
124
+
125
+ end
126
+
@@ -0,0 +1,47 @@
1
+ class DotMatrixReporter < BaseReporter
2
+
3
+ def initialize()
4
+ super()
5
+ self.dots = 0
6
+ self.line_buffer = []
7
+ end
8
+
9
+ defm write(msg)
10
+ if self.dots > 50
11
+ self.writer.writeln(msg)
12
+ self.dots = 0
13
+ else
14
+ self.writer.write(msg)
15
+ end
16
+ end
17
+
18
+ defm on_spec_pass(meta, stats)
19
+ self.dots += 1
20
+ self.write('.')
21
+ end
22
+
23
+ defm on_spec_failure(meta, err, stats)
24
+ self.dots += 1
25
+ self.write(self.to_red("x"))
26
+
27
+ name = meta.get_sentence()
28
+ context = meta.get_context()
29
+ add(self.line_buffer, self.to_red("#{context} ##{name}"))
30
+ add(self.line_buffer, self.to_red(" #{err}"))
31
+ add(self.line_buffer, '')
32
+ end
33
+
34
+ defm on_spec_error(meta, err, stats)
35
+ self.on_spec_failure(meta, err, stats)
36
+ end
37
+
38
+ defm write_epilogue_separator()
39
+ self.writer.writeln('')
40
+ for line in self.line_buffer
41
+ self.writer.writeln(line)
42
+ end
43
+
44
+ self.writer.writeln("")
45
+ end
46
+
47
+ end
@@ -0,0 +1,13 @@
1
+ class MinReporter < BaseReporter
2
+
3
+ defm on_spec_failure(meta, err, stats)
4
+ name = meta.get_sentence()
5
+ context = meta.get_context()
6
+ self.writer.writeln(self.to_red("#{context} ##{name} - #{err}"))
7
+ end
8
+
9
+ defm on_spec_error(meta, err, stats)
10
+ self.on_spec_failure(meta, err, stats)
11
+ end
12
+
13
+ end
@@ -0,0 +1,15 @@
1
+ class ReporterFactory
2
+ defm get_reporter(reporter_name)
3
+ if a:reporter_name == 'spec'
4
+ return new SpecReporter()
5
+ elseif a:reporter_name == 'min'
6
+ return new MinReporter()
7
+ elseif a:reporter_name == 'tap'
8
+ return new TAPReporter()
9
+ elseif a:reporter_name == 'dot'
10
+ return new DotMatrixReporter()
11
+ else
12
+ return new SpecReporter()
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ class SpecReporter < BaseReporter
2
+
3
+ def initialize()
4
+ super()
5
+ self.indents = 0
6
+ end
7
+
8
+ defm write(msg)
9
+ line = "#{self.indent_to_str()}#{msg}"
10
+ self.writer.writeln(line)
11
+ end
12
+
13
+ defm on_start(stats)
14
+ end
15
+
16
+ defm on_context_start(context, stats)
17
+ self.write("#{context}")
18
+ self.indent(1)
19
+ end
20
+
21
+ defm on_context_end(context, stats)
22
+ self.unindent(1)
23
+ end
24
+
25
+ defm on_spec_pass(meta, stats)
26
+ duration_msg = self.get_duration_msg(meta)
27
+ self.write("#{self.get_tick()} #{meta.get_sentence()} #{duration_msg}")
28
+ end
29
+
30
+ defm on_spec_failure(meta, err, stats)
31
+ self.write("#{self.get_cross()} #{self.to_red(meta.get_sentence())}")
32
+ self.indent(2)
33
+ self.write(self.to_red(err))
34
+ self.unindent(2)
35
+ end
36
+
37
+ defm on_spec_error(meta, err, stats)
38
+ self.on_spec_failure(meta, err, stats)
39
+ end
40
+
41
+ defm indent_to_str()
42
+ return repeat(" ", self.indents)
43
+ end
44
+
45
+ defm indent(size)
46
+ self.indents += size
47
+ end
48
+
49
+ defm unindent(size)
50
+ self.indents -= size
51
+ end
52
+
53
+ defm write_epilogue_separator()
54
+ self.writer.writeln('')
55
+ super()
56
+ end
57
+
58
+ end
@@ -0,0 +1,38 @@
1
+ class TAPReporter < BaseReporter
2
+ defm on_start(stats)
3
+ end
4
+
5
+ defm on_spec_start(meta, stats)
6
+ end
7
+
8
+ defm on_spec_end(meta, stats)
9
+ end
10
+
11
+ defm on_spec_pass(meta, stats)
12
+ name = meta.get_sentence()
13
+ duration_msg = self.get_duration_msg(meta)
14
+ preamble = self.get_preamble('ok', stats.get_count(), meta.get_context())
15
+ self.writer.writeln("#{preamble} ##{name} #{duration_msg}")
16
+ end
17
+
18
+ defm on_spec_failure(meta, err, stats)
19
+ name = meta.get_sentence()
20
+ preamble = self.get_preamble('not ok', stats.get_count(), meta.get_context())
21
+ self.writer.writeln("#{preamble} ##{name} - #{err}")
22
+ end
23
+
24
+ defm on_spec_error(meta, err, stats)
25
+ name = meta.get_sentence()
26
+ preamble = self.get_preamble('not ok', stats.get_count(), meta.get_context())
27
+ self.writer.writeln("#{preamble} ##{name} - #{err}")
28
+ end
29
+
30
+ defm on_spec_pending(meta, stats)
31
+ end
32
+
33
+ defm get_preamble(status, count, context)
34
+ ""status = status . repeat(' ', 6 - len(status))
35
+ msg = "#{status} #{count} - #{context}"
36
+ return msg
37
+ end
38
+ end
@@ -0,0 +1,49 @@
1
+ class Runner
2
+ def initialize()
3
+ self.specs = []
4
+ self.halt = false
5
+ self.stopped = false
6
+ self.bail = false
7
+ end
8
+
9
+ defm set_bail(bail)
10
+ self.bail = bail
11
+ end
12
+
13
+ defm get_bail()
14
+ return self.bail
15
+ end
16
+
17
+ defm add(spec)
18
+ add(self.specs, spec)
19
+ end
20
+
21
+ defm start(reporter, stats)
22
+ reporter.on_start(stats)
23
+
24
+ timer = new SpecTimer()
25
+ timer.start()
26
+
27
+ for spec in self.specs
28
+ if self.stopped
29
+ break
30
+ end
31
+
32
+ spec_runner = new SpecRunner(spec)
33
+ spec_runner.set_bail(self.get_bail())
34
+ spec_runner.start(reporter, stats)
35
+
36
+ if spec_runner.has_bailed()
37
+ self.stopped = true
38
+ end
39
+ end
40
+
41
+ timer.stop()
42
+ reporter.on_end(timer.get_duration(), stats)
43
+ end
44
+
45
+ defm stop()
46
+ self.stopped = true
47
+ end
48
+
49
+ end
@@ -0,0 +1,96 @@
1
+ class SpecRunner
2
+
3
+ def initialize(spec)
4
+ self.spec = spec
5
+ self.stopped = false
6
+ self.bail = false
7
+ self.bailed = false
8
+ end
9
+
10
+ defm set_bail(bail)
11
+ self.bail = bail
12
+ end
13
+
14
+ defm has_bailed()
15
+ return self.bailed
16
+ end
17
+
18
+ defm start(reporter, stats)
19
+ spec = self.spec
20
+ did_fail = false
21
+ context = self.call_hook('describe')
22
+ reporter.on_context_start(context, stats)
23
+ self.call_hook('before')
24
+
25
+ for method in keys(spec)
26
+ if self.stopped
27
+ break
28
+ end
29
+
30
+ if method =~ '^it'
31
+ timer = new SpecTimer()
32
+ meta = new SpecMeta(context, method)
33
+
34
+ reporter.on_spec_start(meta, stats)
35
+ result = 0
36
+
37
+ try
38
+ timer.start()
39
+
40
+ self.call_hook('before_each')
41
+ eval("spec.#{method}()")
42
+ self.call_hook('after_each')
43
+
44
+ timer.stop()
45
+ meta.set_duration(timer.get_duration())
46
+
47
+ stats.inc_passes()
48
+ reporter.on_spec_pass(meta, stats)
49
+ catch /Unknown function.*expect/
50
+ did_fail = true
51
+ stats.inc_failures()
52
+ exception = 'DSLError: expect() not found, dsl.riml may not be included'
53
+ reporter.on_spec_failure(meta, exception, stats)
54
+ catch /Unknown function.*define_matcher/
55
+ did_fail = true
56
+ stats.inc_failures()
57
+ exception = 'DSLError: define_matcher() not found, dsl.riml may not be included'
58
+ reporter.on_spec_failure(meta, exception, stats)
59
+ catch /^AssertionError/
60
+ did_fail = true
61
+ stats.inc_failures()
62
+ reporter.on_spec_failure(meta, v:exception, stats)
63
+ catch /.*/
64
+ did_fail = true
65
+ stats.inc_errors()
66
+ reporter.on_spec_error(meta, v:exception, stats)
67
+ end
68
+
69
+ reporter.on_spec_end(meta, stats)
70
+ :redraw
71
+
72
+ if did_fail && self.bail
73
+ self.bailed = true
74
+ break
75
+ end
76
+ end
77
+ end
78
+
79
+ self.call_hook('after')
80
+ reporter.on_context_end(context, stats)
81
+ end
82
+
83
+ defm call_hook(hook)
84
+ spec = self.spec
85
+ if has_key(spec, hook)
86
+ return eval("spec.#{hook}()");
87
+ else
88
+ return "Undefined hook: #{hook}"
89
+ end
90
+ end
91
+
92
+ defm stop()
93
+ self.stopped = true
94
+ end
95
+
96
+ end
@@ -0,0 +1,18 @@
1
+ module Speckle
2
+ module CLI
3
+
4
+ require_relative 'environment'
5
+ require_relative 'router'
6
+
7
+ class App
8
+ def start(args)
9
+ env = Environment.new
10
+ options = env.load(args)
11
+
12
+ router = Router.new
13
+ router.route(options.action, options)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,67 @@
1
+ module Speckle
2
+ module CLI
3
+
4
+ require 'speckle/version'
5
+
6
+ class Controller
7
+
8
+ def initialize(options, rake_app)
9
+ @options = options
10
+ @rake_app = rake_app
11
+ end
12
+
13
+ def rake(task)
14
+ @rake_app.invoke_task(task)
15
+ end
16
+
17
+ def show_version
18
+ puts VERSION
19
+ end
20
+
21
+ def show_help
22
+ puts @options.opts
23
+ end
24
+
25
+ def show_error(msg = @options.error)
26
+ puts "Error: #{msg}"
27
+ puts
28
+
29
+ show_help
30
+ end
31
+
32
+ def show_invalid_option
33
+ show_error @options.error
34
+ end
35
+
36
+ def show_missing_args
37
+ show_error @options.error
38
+ end
39
+
40
+ def show_parser_error
41
+ show_error @options.error
42
+ end
43
+
44
+ def show_no_spec_dir
45
+ show_error '"spec" directory not found'
46
+ end
47
+
48
+ def compile
49
+ rake :compile_tests
50
+ end
51
+
52
+ def compile_and_test
53
+ rake :compile_and_test
54
+ end
55
+
56
+ def test
57
+ rake :test
58
+ end
59
+
60
+ def watch
61
+ puts '--- TODO ---'
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+ end