petitest 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/CHANGELOG.md +3 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +105 -0
  7. data/Rakefile +6 -0
  8. data/lib/petitest/assertion_failure_error.rb +4 -0
  9. data/lib/petitest/assertion_failure_message.rb +35 -0
  10. data/lib/petitest/assertions.rb +46 -0
  11. data/lib/petitest/autorun.rb +8 -0
  12. data/lib/petitest/configuration.rb +26 -0
  13. data/lib/petitest/subscribers/base_subscriber.rb +21 -0
  14. data/lib/petitest/subscribers/json_report_subscriber.rb +38 -0
  15. data/lib/petitest/subscribers/progress_report_subscriber.rb +23 -0
  16. data/lib/petitest/subscribers/timer_subscriber.rb +26 -0
  17. data/lib/petitest/test_case.rb +125 -0
  18. data/lib/petitest/test_cases_runner.rb +58 -0
  19. data/lib/petitest/test_group.rb +51 -0
  20. data/lib/petitest/test_method.rb +25 -0
  21. data/lib/petitest/texts/base_text.rb +54 -0
  22. data/lib/petitest/texts/error_message_text.rb +20 -0
  23. data/lib/petitest/texts/errors_element_text.rb +54 -0
  24. data/lib/petitest/texts/errors_text.rb +45 -0
  25. data/lib/petitest/texts/failure_message_text.rb +20 -0
  26. data/lib/petitest/texts/failures_element_text.rb +54 -0
  27. data/lib/petitest/texts/failures_text.rb +45 -0
  28. data/lib/petitest/texts/filtered_backtrace_text.rb +20 -0
  29. data/lib/petitest/texts/raised_code_text.rb +48 -0
  30. data/lib/petitest/texts/test_case_result_character_text.rb +29 -0
  31. data/lib/petitest/texts/test_cases_result_margin_top_text.rb +24 -0
  32. data/lib/petitest/texts/test_cases_result_text.rb +45 -0
  33. data/lib/petitest/texts/test_counts_text.rb +96 -0
  34. data/lib/petitest/texts/times_text.rb +47 -0
  35. data/lib/petitest/version.rb +3 -0
  36. data/lib/petitest.rb +36 -0
  37. data/petitest.gemspec +24 -0
  38. metadata +108 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ad7132e7dbbfaf374485b3198b4e0d0950272f10
4
+ data.tar.gz: be7660bd9bca8b83d12b0bac75bc7c7f4a870a2b
5
+ SHA512:
6
+ metadata.gz: c9290292fe37e1f0f44729e798900b17c2f151e4bd218da5878de2293549b3ea04532612c482096af2f78c9794a6ef51687cb50cfb2118f40a73eaeaea3fda64
7
+ data.tar.gz: 9cbf07c7adb70c3b8e73e1febcd7b537642c6b317f82145f2455647d90b065045aa494368e07c687a8ea311616671b28b8ac2d48b521393f3076585c6e9cd644
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## v0.1.0
2
+
3
+ - 1st Release :tada:
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in petitest.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Ryo Nakamura
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # Petitest
2
+
3
+ A minimal solid testing framework for Ruby.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem "petitest"
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ gem install petitest
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### 1. Write test cases
28
+
29
+ Define a child class of `Petitest::TestGroup` with `#test_xxx` methods.
30
+
31
+ ```ruby
32
+ # test/some_test.rb
33
+ require "petitest/autorun"
34
+
35
+ class Sometest < Petitest::TestGroup
36
+ def test_empty_string
37
+ assert("")
38
+ end
39
+
40
+ def test_false
41
+ assert(false)
42
+ end
43
+
44
+ def test_nil
45
+ assert(nil)
46
+ end
47
+
48
+ def test_raise
49
+ raise
50
+ end
51
+
52
+ def test_true
53
+ assert(true)
54
+ end
55
+
56
+ def test_zero
57
+ assert(0)
58
+ end
59
+ end
60
+ ```
61
+
62
+ ### 2. Run tests
63
+
64
+ Run your test file as a Ruby script:
65
+
66
+ ```bash
67
+ ruby test/sometest_test.rb
68
+ ```
69
+
70
+ ```
71
+ .FFE..
72
+
73
+ Failures:
74
+
75
+ 1) Sometest#test_false
76
+ assert(false)
77
+ false is not truthy
78
+ # test/sometest_test.rb:9:in `test_false'
79
+
80
+ 2) Sometest#test_nil
81
+ assert(nil)
82
+ nil is not truthy
83
+ # test/sometest_test.rb:13:in `test_nil'
84
+
85
+ Errors:
86
+
87
+ 1) Sometest#test_raise
88
+ raise
89
+ RuntimeError:
90
+ # test/sometest_test.rb:17:in `test_raise'
91
+
92
+ Counts:
93
+
94
+ 6 tests
95
+ 3 passes
96
+ 2 failures
97
+ 1 errors
98
+ 0 skips
99
+
100
+ Times:
101
+
102
+ Started: 2017-03-22T05:40:32.846640+09:00
103
+ Finished: 2017-03-22T05:40:32.846784+09:00
104
+ Total: 0.000144s
105
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,4 @@
1
+ module Petitest
2
+ class AssertionFailureError < ::StandardError
3
+ end
4
+ end
@@ -0,0 +1,35 @@
1
+ module Petitest
2
+ class AssertionFailureMessage
3
+ # @return [String]
4
+ attr_reader :template
5
+
6
+ # @return [Hash{Symbol => Object}]
7
+ attr_reader :template_variables
8
+
9
+ # @return [String, nil]
10
+ attr_reader :user_specified_message
11
+
12
+ # @param template [String]
13
+ # @param template_variables [Hash{Symbol => Object}]
14
+ # @param user_specified_message [String, nil]
15
+ def initialize(template:, template_variables: {}, user_specified_message:)
16
+ @template = template
17
+ @template_variables = template_variables
18
+ @user_specified_message = user_specified_message
19
+ end
20
+
21
+ # @note Override
22
+ def to_s
23
+ template % inspected_template_variables
24
+ end
25
+
26
+ private
27
+
28
+ # @return [Hash{Symbol => String}]
29
+ def inspected_template_variables
30
+ template_variables.map do |key, value|
31
+ [key, value.inspect]
32
+ end.to_h
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ module Petitest
2
+ module Assertions
3
+ # @param actual [Object]
4
+ # @param user_specified_message [String, nil]
5
+ def assert(actual, user_specified_message = nil)
6
+ check(
7
+ assertion_failure_message: ::Petitest::AssertionFailureMessage.new(
8
+ template: "%{actual} is not truthy",
9
+ template_variables: {
10
+ actual: actual,
11
+ },
12
+ user_specified_message: user_specified_message,
13
+ ),
14
+ result: actual,
15
+ )
16
+ end
17
+
18
+ # @param expected [Object]
19
+ # @param actual [Object]
20
+ # @param user_specified_message [String, nil]
21
+ def assert_equal(expected, actual, user_specified_message = nil)
22
+ result = expected == actual
23
+ check(
24
+ assertion_failure_message: ::Petitest::AssertionFailureMessage.new(
25
+ template: "%{expected} expected but was %{actual}",
26
+ template_variables: {
27
+ actual: actual,
28
+ expected: expected,
29
+ },
30
+ user_specified_message: user_specified_message,
31
+ ),
32
+ result: result,
33
+ )
34
+ end
35
+
36
+ private
37
+
38
+ # @param assertion_failure_message [Petitest::AssertionFailureMessage]
39
+ # @param result [Boolean]
40
+ def check(assertion_failure_message:, result:)
41
+ unless result
42
+ raise ::Petitest::AssertionFailureError.new(assertion_failure_message)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,8 @@
1
+ require "petitest"
2
+
3
+ at_exit do
4
+ test_cases = Petitest::TestGroup.test_cases
5
+ result = Petitest::TestCasesRunner.new(test_cases).run
6
+ exit_code = result ? 0 : 1
7
+ exit(exit_code)
8
+ end
@@ -0,0 +1,26 @@
1
+ module Petitest
2
+ class Configuration
3
+ DEFAULT_COLOR_SCHEME = {
4
+ detail: :cyan,
5
+ error: :red,
6
+ failure: :red,
7
+ pass: :green,
8
+ skip: :yellow,
9
+ }
10
+
11
+ # @return [Hash{Symbol => Symbol}]
12
+ def color_scheme
13
+ @color_scheme ||= DEFAULT_COLOR_SCHEME.dup
14
+ end
15
+
16
+ # @return [Boolean]
17
+ def colored
18
+ true
19
+ end
20
+
21
+ # @return [Array<Petitest::Subscribers::BaseSubscriber>]
22
+ def subscribers
23
+ @subscribers ||= [::Petitest::Subscribers::ProgressReportSubscriber.new]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ module Petitest
2
+ module Subscribers
3
+ class BaseSubscriber
4
+ # @param test_case [Petit::TestCase]
5
+ def after_running_test_case(test_case)
6
+ end
7
+
8
+ # @param test_cases [Array<Petit::TestCase>]
9
+ def after_running_test_cases(test_cases)
10
+ end
11
+
12
+ # @param test_case [Petit::TestCase]
13
+ def before_running_test_case(test_case)
14
+ end
15
+
16
+ # @param test_cases [Array<Petit::TestCase>]
17
+ def before_running_test_cases(test_cases)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ require "json"
2
+ require "petitest/subscribers/timer_subscriber"
3
+
4
+ module Petitest
5
+ module Subscribers
6
+ class JsonReportSubscriber < ::Petitest::Subscribers::TimerSubscriber
7
+ # @note Override
8
+ def after_running_test_cases(test_cases)
9
+ super
10
+ data = {
11
+ test_cases: test_cases.map do |test_case|
12
+ {
13
+ aborted: test_case.aborted?,
14
+ backtrace: test_case.backtrace,
15
+ class_name: test_case.test_group_class.to_s,
16
+ error_class_name: test_case.error_class_name,
17
+ error_message: test_case.error_message,
18
+ failed: test_case.failed?,
19
+ failure_message: test_case.failure_message,
20
+ finished_at: test_case.finished_at.iso8601(6),
21
+ method_line_number: test_case.test_method.line_number,
22
+ method_name: test_case.test_method.method_name,
23
+ path: test_case.test_method.path,
24
+ skipped: test_case.skipped?,
25
+ started_at: test_case.started_at.iso8601(6),
26
+ }
27
+ end,
28
+ times: {
29
+ finished_at: finished_at.iso8601(6),
30
+ started_at: started_at.iso8601(6),
31
+ },
32
+ }
33
+ output = ::JSON.pretty_generate(data)
34
+ puts output
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ require "petitest/subscribers/timer_subscriber"
2
+
3
+ module Petitest
4
+ module Subscribers
5
+ class ProgressReportSubscriber < ::Petitest::Subscribers::TimerSubscriber
6
+ # @note Override
7
+ def after_running_test_case(test_case)
8
+ super
9
+ print ::Petitest::Texts::TestCaseResultCharacterText.new(test_case: test_case)
10
+ end
11
+
12
+ # @note Override
13
+ def after_running_test_cases(test_cases)
14
+ super
15
+ puts ::Petitest::Texts::TestCasesResultText.new(
16
+ finished_at: finished_at,
17
+ started_at: started_at,
18
+ test_cases: test_cases,
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ require "petitest/subscribers/base_subscriber"
2
+ require "time"
3
+
4
+ module Petitest
5
+ module Subscribers
6
+ class TimerSubscriber < ::Petitest::Subscribers::BaseSubscriber
7
+ # @return [Time, nil]
8
+ attr_accessor :finished_at
9
+
10
+ # @return [Time, nil]
11
+ attr_accessor :started_at
12
+
13
+ # @note Override
14
+ def after_running_test_cases(test_cases)
15
+ super
16
+ self.finished_at = ::Time.now
17
+ end
18
+
19
+ # @note Override
20
+ def before_running_test_cases(test_cases)
21
+ super
22
+ self.started_at = ::Time.now
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,125 @@
1
+ module Petitest
2
+ class TestCase
3
+ # @return [StandardError, nil]
4
+ attr_accessor :error
5
+
6
+ # @return [Time, nil]
7
+ attr_accessor :finished_at
8
+
9
+ # @return [Time, nil]
10
+ attr_accessor :started_at
11
+
12
+ # @return [Class]
13
+ attr_reader :test_group_class
14
+
15
+ # @return [Petitest::TestMethod]
16
+ attr_reader :test_method
17
+
18
+ class << self
19
+ # @return [String]
20
+ def prefix_to_filter_backtrace
21
+ @prefix_to_filter_backtrace ||= ::File.expand_path("../..", __FILE__)
22
+ end
23
+ end
24
+
25
+ # @param test_group_class [Class]
26
+ # @param test_method [Petitest::TestMethod]
27
+ def initialize(
28
+ test_group_class:,
29
+ test_method:
30
+ )
31
+ @duration = nil
32
+ @processed = false
33
+ @test_group_class = test_group_class
34
+ @test_method = test_method
35
+ end
36
+
37
+ # @return [Boolean]
38
+ def aborted?
39
+ processed? && has_error? && !has_assertion_error?
40
+ end
41
+
42
+ # @return [Array<String>, nil]
43
+ def backtrace
44
+ if has_error?
45
+ error.backtrace
46
+ end
47
+ end
48
+
49
+ # @return [String, nil]
50
+ def error_class_name
51
+ if aborted?
52
+ error.class.to_s
53
+ end
54
+ end
55
+
56
+ # @return [String, nil]
57
+ def error_message
58
+ if aborted?
59
+ error.to_s
60
+ end
61
+ end
62
+
63
+ # @return [String, nil]
64
+ def failure_message
65
+ if failed?
66
+ error.to_s
67
+ end
68
+ end
69
+
70
+ # @return [Boolean]
71
+ def failed?
72
+ processed? && has_assertion_error?
73
+ end
74
+
75
+ # @return [Array<String>, nil]
76
+ def filtered_backtrace
77
+ @filtered_backtrace ||= backtrace.reject do |line|
78
+ line.start_with?(self.class.prefix_to_filter_backtrace)
79
+ end
80
+ end
81
+
82
+ # @return [Boolean]
83
+ def has_assertion_error?
84
+ error.is_a?(::Petitest::AssertionFailureError)
85
+ end
86
+
87
+ # @return [Boolean]
88
+ def has_error?
89
+ !error.nil?
90
+ end
91
+
92
+ # @return [Boolean]
93
+ def passed?
94
+ processed? && error.nil?
95
+ end
96
+
97
+ # @return [Boolean]
98
+ def processed?
99
+ @processed
100
+ end
101
+
102
+ def run
103
+ self.started_at = ::Time.now
104
+ test_group_class.new.send(test_method.method_name)
105
+ true
106
+ rescue => error
107
+ self.error = error
108
+ false
109
+ ensure
110
+ @finished_at = ::Time.now
111
+ @processed = true
112
+ end
113
+
114
+ # @todo
115
+ # @return [Boolean]
116
+ def skipped?
117
+ false
118
+ end
119
+
120
+ # @return [String]
121
+ def test_signature
122
+ [test_group_class, test_method.method_name].join("#")
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,58 @@
1
+ module Petitest
2
+ class TestCasesRunner
3
+ # @return [Array<Petitest::TestCase>]
4
+ attr_reader :test_cases
5
+
6
+ # @param test_cases [Array<Petitest::TestCase>]
7
+ def initialize(test_cases)
8
+ @test_cases = test_cases
9
+ end
10
+
11
+ # @return [Boolean]
12
+ def run
13
+ before_running_test_cases(test_cases)
14
+ test_cases.each do |test_case|
15
+ before_running_test_case(test_case)
16
+ test_case.run
17
+ after_running_test_case(test_case)
18
+ end
19
+ after_running_test_cases(test_cases)
20
+ test_cases.all?(&:passed?)
21
+ end
22
+
23
+ private
24
+
25
+ # @param test_case [Petitest::TestCase]
26
+ def after_running_test_case(test_case)
27
+ subscribers.each do |subscriber|
28
+ subscriber.after_running_test_case(test_case)
29
+ end
30
+ end
31
+
32
+ # @param test_cases [Array<Petitest::TestCase>]
33
+ def after_running_test_cases(test_cases)
34
+ subscribers.each do |subscriber|
35
+ subscriber.after_running_test_cases(test_cases)
36
+ end
37
+ end
38
+
39
+ # @param test_case [Petitest::TestCase]
40
+ def before_running_test_case(test_case)
41
+ subscribers.each do |subscriber|
42
+ subscriber.before_running_test_case(test_case)
43
+ end
44
+ end
45
+
46
+ # @param test_cases [Array<Petitest::TestCase>]
47
+ def before_running_test_cases(test_cases)
48
+ subscribers.each do |subscriber|
49
+ subscriber.before_running_test_cases(test_cases)
50
+ end
51
+ end
52
+
53
+ # @return [Array<Petitest::Subscribers::BaseSubscriber>]
54
+ def subscribers
55
+ ::Petitest.configuration.subscribers
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,51 @@
1
+ module Petitest
2
+ class TestGroup
3
+ include ::Petitest::Assertions
4
+
5
+ TEST_METHOD_NAME_PREFIX = "test_"
6
+
7
+ class << self
8
+ # @return [Array<Class>]
9
+ def descendants
10
+ @@descendants ||= []
11
+ end
12
+
13
+ # @note Override
14
+ def inherited(sub_class)
15
+ super
16
+ descendants << sub_class
17
+ end
18
+
19
+ # @return [Array<Petit::TestCase]
20
+ def test_cases
21
+ descendants.flat_map do |test_group_class|
22
+ test_group_class.test_methods.map do |test_method|
23
+ ::Petitest::TestCase.new(
24
+ test_group_class: test_group_class,
25
+ test_method: test_method,
26
+ )
27
+ end
28
+ end
29
+ end
30
+
31
+ # @return [Array<String>]
32
+ def test_method_names
33
+ public_instance_methods.map(&:to_s).select do |method_name|
34
+ method_name.start_with?(TEST_METHOD_NAME_PREFIX)
35
+ end
36
+ end
37
+
38
+ # @return [Array<Petitest::TestMethod>]
39
+ def test_methods
40
+ test_method_names.map do |method_name|
41
+ unbound_method = public_instance_method(method_name)
42
+ ::Petitest::TestMethod.new(
43
+ line_number: unbound_method.source_location[1],
44
+ method_name: method_name.to_s,
45
+ path: unbound_method.source_location[0],
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,25 @@
1
+ module Petitest
2
+ class TestMethod
3
+ # @return [Integer]
4
+ attr_reader :line_number
5
+
6
+ # @return [String]
7
+ attr_reader :method_name
8
+
9
+ # @return [String]
10
+ attr_reader :path
11
+
12
+ # @param line_number [Integer]
13
+ # @param method_name [String]
14
+ # @param path [String]
15
+ def initialize(
16
+ line_number:,
17
+ method_name:,
18
+ path:
19
+ )
20
+ @line_number = line_number
21
+ @method_name = method_name
22
+ @path = path
23
+ end
24
+ end
25
+ end