gloo-test 1.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4a73cec30908a847f9a57eff2a51260510ce2c6dae9c7ce3a5c899cb2b687385
4
+ data.tar.gz: 3e39c1eeac6b251ed68f81047759a7a505d5eccff6c62b3c156994a8c42b5a8c
5
+ SHA512:
6
+ metadata.gz: a78397cbeb4554166ec1d210149969121f1ebe133f3d6b7c45811fba1da0c1f3f2c2627a0a12dfa2257b3a6e9b4a85294fd61f42c53b8731cf201e33a29b6e5a
7
+ data.tar.gz: b11b5be346c5de429676e3095c675b4ef651fc4ea4688890473fba113bcd1345777175ee95011ef4f930fe5d6054bbe8c751f3987df1e3ce5d347f28d9a83c29
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # gloo-test
2
+
3
+ A core gloo library for unit testing gloo code.
4
+
5
+ ## Usage
6
+
7
+ Loaded automatically when running gloo in test mode:
8
+
9
+ ```gloo
10
+ > gloo --test ./path/to/tests/
11
+ ```
12
+
13
+ It can also be loaded explicitly in a script, like any other core library:
14
+
15
+ ```gloo
16
+ > load lib test
17
+ ```
18
+
19
+ ## Objects
20
+
21
+ - `test` — a single gloo test
22
+
23
+ ## Verbs
24
+
25
+ - `assert` — assert an expectation about the state or result of a prior command
26
+ - `refute` — refute an expectation about the state or result of a prior command
27
+
28
+ ## Full Reference
29
+
30
+ Every object/verb's full syntax is documented in-app once the library is loaded — e.g. `help> verb assert`.
data/lib/assert.rb ADDED
@@ -0,0 +1,88 @@
1
+ #
2
+ # Assert that [it] is true.
3
+ #
4
+ class Assert < Gloo::Core::Verb
5
+
6
+ KEYWORD = 'assert'.freeze
7
+ KEYWORD_SHORT = 'expect'.freeze
8
+ DEFAULT_MESSAGE = 'Assertion failed'.freeze
9
+
10
+ #
11
+ # Get the Verb's keyword.
12
+ #
13
+ def self.keyword
14
+ return KEYWORD
15
+ end
16
+
17
+ #
18
+ # Get the Verb's keyword shortcut.
19
+ #
20
+ def self.keyword_shortcut
21
+ return KEYWORD_SHORT
22
+ end
23
+
24
+ #
25
+ # Run the verb.
26
+ #
27
+ def run
28
+ begin
29
+ @engine.context_object.assert_count += 1
30
+ if @engine.heap.it.is_true?
31
+ # Assertion passes
32
+ @engine.context_object.passed = true
33
+ return true
34
+ else
35
+ # Assertion fails
36
+ @engine.context_object.passed = false
37
+ @engine.context_object.add_message get_message
38
+ return false
39
+ end
40
+ rescue => ex
41
+ @engine.log_exception ex
42
+ end
43
+ end
44
+
45
+ #
46
+ # Get the assertion message.
47
+ #
48
+ def get_message
49
+ if @tokens.token_count > 1
50
+ expr = Gloo::Expr::Expression.new( @engine, @tokens.params )
51
+ result = expr.evaluate
52
+ return result
53
+ end
54
+ return DEFAULT_MESSAGE
55
+ end
56
+
57
+ # ---------------------------------------------------------------------
58
+ # Verb Documentation
59
+ # ---------------------------------------------------------------------
60
+
61
+ #
62
+ # Get the verb's documentation data.
63
+ #
64
+ def self.doc_data
65
+ {
66
+ :name => KEYWORD,
67
+ :shortcut => KEYWORD_SHORT,
68
+ :description => 'Assert an expectation about the state or ' \
69
+ 'results. The verb looks at the value of it. If it is true, ' \
70
+ 'then the assertion passes, otherwise it fails.',
71
+ :syntax => [ 'assert {optional expectation message}' ],
72
+ :parameters => [
73
+ "optional expectation message — A textual statement about " \
74
+ "what was expected. Optional. If none provided the default " \
75
+ "\"#{DEFAULT_MESSAGE}\" will be shown."
76
+ ],
77
+ :result => 'Validates an assumption and reports failures (or success).',
78
+ :examples => <<~EXAMPLES.strip
79
+ assert_noop [test] :
80
+ description [string] : Assert no operation
81
+ on_test [script] :
82
+ noop
83
+ assert 'noop should be true'
84
+ EXAMPLES
85
+ }
86
+ end
87
+
88
+ end
data/lib/gloo-test.rb ADDED
@@ -0,0 +1,29 @@
1
+ #
2
+ # Shim to allow `require 'gloo-test'`
3
+ #
4
+ # This file is loaded when someone does `require 'gloo-test'`
5
+ #
6
+ require 'assert'
7
+ require 'refute'
8
+ require 'result'
9
+ require 'results'
10
+ require 'test_file'
11
+ require 'test_files'
12
+ require 'test_runner'
13
+ require 'test'
14
+
15
+ #
16
+ # Registers the extension.
17
+ #
18
+ class TestInit < Gloo::Plugin::Base
19
+
20
+ #
21
+ # Register verbs and objects.
22
+ #
23
+ def register( callback )
24
+ callback.register_obj( Test )
25
+ callback.register_verb( Assert )
26
+ callback.register_verb( Refute )
27
+ end
28
+
29
+ end
data/lib/refute.rb ADDED
@@ -0,0 +1,89 @@
1
+ #
2
+ # Refute that [it] is true.
3
+ # (Assert that [it] is false.)
4
+ #
5
+ class Refute < Gloo::Core::Verb
6
+
7
+ KEYWORD = 'refute'.freeze
8
+ KEYWORD_SHORT = 'expect_not'.freeze
9
+ DEFAULT_MESSAGE = 'Refutation failed'.freeze
10
+
11
+ #
12
+ # Get the Verb's keyword.
13
+ #
14
+ def self.keyword
15
+ return KEYWORD
16
+ end
17
+
18
+ #
19
+ # Get the Verb's keyword shortcut.
20
+ #
21
+ def self.keyword_shortcut
22
+ return KEYWORD_SHORT
23
+ end
24
+
25
+ #
26
+ # Run the verb.
27
+ #
28
+ def run
29
+ begin
30
+ @engine.context_object.refute_count += 1
31
+ if @engine.heap.it.is_false?
32
+ # Refutation passes
33
+ @engine.context_object.passed = true
34
+ return true
35
+ else
36
+ # Refutation fails
37
+ @engine.context_object.passed = false
38
+ @engine.context_object.add_message(get_message)
39
+ return false
40
+ end
41
+ rescue => ex
42
+ @engine.log_exception ex
43
+ end
44
+ end
45
+
46
+ #
47
+ # Get the refutation message.
48
+ #
49
+ def get_message
50
+ if @tokens.token_count > 1
51
+ expr = Gloo::Expr::Expression.new( @engine, @tokens.params )
52
+ result = expr.evaluate
53
+ return result
54
+ end
55
+ return DEFAULT_MESSAGE
56
+ end
57
+
58
+ # ---------------------------------------------------------------------
59
+ # Verb Documentation
60
+ # ---------------------------------------------------------------------
61
+
62
+ #
63
+ # Get the verb's documentation data.
64
+ #
65
+ def self.doc_data
66
+ {
67
+ :name => KEYWORD,
68
+ :shortcut => KEYWORD_SHORT,
69
+ :description => 'Refute an expectation about the state or ' \
70
+ 'results. The verb looks at the value of it. If it is false, ' \
71
+ 'then the assertion passes, otherwise it fails.',
72
+ :syntax => [ 'refute {optional expectation message}' ],
73
+ :parameters => [
74
+ "optional expectation message — A textual statement about " \
75
+ "what was NOT expected. Optional. If none provided the " \
76
+ "default \"#{DEFAULT_MESSAGE}\" will be shown."
77
+ ],
78
+ :result => 'Validates a negative assumption and reports failures (or success).',
79
+ :examples => <<~EXAMPLES.strip
80
+ refute [test] :
81
+ description [string] : Refute an operation
82
+ on_test [script] :
83
+ eval false
84
+ refute 'false should be false'
85
+ EXAMPLES
86
+ }
87
+ end
88
+
89
+ end
data/lib/result.rb ADDED
@@ -0,0 +1,44 @@
1
+ #
2
+ # A gloo unit test result object.
3
+ # The result of a single test.
4
+ #
5
+ class Result
6
+
7
+ attr_accessor :passed, :assert_count, :refute_count
8
+
9
+ def initialize( engine, test )
10
+ @engine = engine
11
+ @test = test
12
+ @test_desc = test.test_desc
13
+ @pn = test.pn
14
+
15
+ @assert_count = 0
16
+ @refute_count = 0
17
+
18
+ @passed = true
19
+ @failure_msg = ''
20
+ end
21
+
22
+ #
23
+ # Show the test result symbol.
24
+ #
25
+ def show_result_symbol
26
+ print @passed ? '.' : 'x'
27
+ end
28
+
29
+ #
30
+ # Add a failure message to the result.
31
+ #
32
+ def add_message(message)
33
+ @failure_msg += " " + message + "\n"
34
+ end
35
+
36
+ #
37
+ # Show the failure message.
38
+ #
39
+ def show_failure
40
+ puts "#{@pn} -> #{@test_desc}"
41
+ puts @failure_msg
42
+ puts
43
+ end
44
+ end
data/lib/results.rb ADDED
@@ -0,0 +1,114 @@
1
+ #
2
+ # A gloo unit test results collection.
3
+ # The results of a the set of tests run.
4
+ #
5
+ class Results
6
+
7
+ attr_accessor :file_count, :test_count,
8
+ :pass_count, :fail_count, :assert_count
9
+
10
+ #
11
+ # Set up the results collection.
12
+ #
13
+ def initialize( engine )
14
+ @engine = engine
15
+
16
+ @all_results = []
17
+ @failures = []
18
+
19
+ @file_count = 0
20
+ @pass_count = 0
21
+ @fail_count = 0
22
+ @assert_count = 0
23
+
24
+ @engine.log.debug "Results initialized"
25
+ end
26
+
27
+ # ---------------------------------------------------------------------
28
+ # Timer
29
+ # ---------------------------------------------------------------------
30
+
31
+ #
32
+ # Set a timer to track test duration.
33
+ #
34
+ def start_timer
35
+ @start_time = Time.now
36
+ end
37
+
38
+ #
39
+ # End the timer.
40
+ #
41
+ def end_timer
42
+ @end_time = Time.now
43
+ end
44
+
45
+ #
46
+ # Get the duration of the test.
47
+ #
48
+ def duration
49
+ return @end_time - @start_time
50
+ end
51
+
52
+
53
+ # ---------------------------------------------------------------------
54
+ # Results
55
+ # ---------------------------------------------------------------------
56
+
57
+ #
58
+ # Add a result to the collection.
59
+ #
60
+ def add_result( result )
61
+ @all_results << result
62
+ @failures << result unless result.passed
63
+
64
+ @pass_count += 1 if result.passed
65
+ @fail_count += 1 unless result.passed
66
+ @assert_count += result.assert_count
67
+ @assert_count += result.refute_count
68
+ end
69
+
70
+
71
+ # ---------------------------------------------------------------------
72
+ # Show Results
73
+ # ---------------------------------------------------------------------
74
+
75
+ #
76
+ # Show the results.
77
+ #
78
+ def show_results
79
+ delta = duration.round( 2 )
80
+ puts
81
+ puts get_result_summary
82
+ puts "Tests finished in #{delta} seconds".white
83
+ puts
84
+ end
85
+
86
+ #
87
+ # Show the failures.
88
+ #
89
+ def show_failures
90
+ if @fail_count > 0
91
+ puts
92
+ puts "*** Failures (#{@fail_count}) ***".red
93
+ puts
94
+ @failures.each do |failure|
95
+ failure.show_failure
96
+ end
97
+ end
98
+ end
99
+
100
+ #
101
+ # Get a textual summary of the results.
102
+ #
103
+ def get_result_summary
104
+ str = "Tests: #{@all_results.length} • Passed: #{@pass_count} • ".white
105
+ if @fail_count > 0
106
+ str += " Failed: #{@fail_count} ".red
107
+ else
108
+ str += " Failed: #{@fail_count} ".white
109
+ end
110
+ str += "\n Assertions: #{@assert_count} • Files: #{@file_count}".yellow
111
+ return str
112
+ end
113
+
114
+ end
data/lib/test.rb ADDED
@@ -0,0 +1,126 @@
1
+ #
2
+ # A gloo unit test object
3
+ #
4
+ class Test < Gloo::Core::Obj
5
+
6
+ KEYWORD = 'test'.freeze
7
+ TEST_DESC = 'description'.freeze
8
+ ON_TEST_EVENT = 'on_test'.freeze
9
+
10
+
11
+ #
12
+ # The name of the object type.
13
+ #
14
+ def self.typename
15
+ return KEYWORD
16
+ end
17
+
18
+ #
19
+ # The short name of the object type.
20
+ # Same as the typename.
21
+ #
22
+ def self.short_typename
23
+ return KEYWORD
24
+ end
25
+
26
+ #
27
+ # Get the test description.
28
+ #
29
+ def test_desc
30
+ o = find_child TEST_DESC
31
+ return o ? o.value : 'Unknown'
32
+ end
33
+
34
+
35
+ # ---------------------------------------------------------------------
36
+ # Children
37
+ # ---------------------------------------------------------------------
38
+
39
+ # Does this object have children to add when an object
40
+ # is created in interactive mode?
41
+ # This does not apply during obj load, etc.
42
+ def add_children_on_create?
43
+ return true
44
+ end
45
+
46
+ # Add children to this object.
47
+ # This is used by containers to add children needed
48
+ # for default configurations.
49
+ def add_default_children
50
+ fac = @engine.factory
51
+ fac.create_string TEST_DESC, '', self
52
+ fac.create_script ON_TEST_EVENT, '', self
53
+ end
54
+
55
+
56
+ # ---------------------------------------------------------------------
57
+ # Messages
58
+ # ---------------------------------------------------------------------
59
+
60
+ #
61
+ # Get a list of message names that this object receives.
62
+ #
63
+ def self.messages
64
+ return super # + [ 'run' ]
65
+ end
66
+
67
+ #
68
+ # Run the test.
69
+ #
70
+ def run_test
71
+ result = Result.new( @engine, self )
72
+ @engine.context_object = result
73
+ run_on_test
74
+ @engine.context_object = nil
75
+
76
+ result.show_result_symbol
77
+
78
+ return result
79
+ end
80
+
81
+ #
82
+ # Run the on_test script.
83
+ #
84
+ def run_on_test
85
+ o = find_child ON_TEST_EVENT
86
+ return unless o
87
+
88
+ Gloo::Exec::Dispatch.message( @engine, 'run', o )
89
+ end
90
+
91
+ # ---------------------------------------------------------------------
92
+ # Object Documentation
93
+ # ---------------------------------------------------------------------
94
+
95
+ #
96
+ # Get the object's documentation data.
97
+ #
98
+ def self.doc_data
99
+ {
100
+ :name => KEYWORD,
101
+ :shortcut => KEYWORD,
102
+ :description => 'A single gloo test.',
103
+ :children => [
104
+ 'description (string) — A textual description of the test and desired outcome, used in output if there is an error.',
105
+ 'on_test (script) — The script to run when the test is executed. Note that the normal way to run tests is with gloo --test.'
106
+ ],
107
+ :examples => <<~EXAMPLES.strip
108
+ #
109
+ # Basic tests
110
+ #
111
+ tests [can] :
112
+ basic [can] :
113
+
114
+ on_load [script] :
115
+ log 'Include functionality for all test in on_load' (debug)
116
+
117
+ assert_noop [test] :
118
+ description [string] : Assert no operation
119
+ on_test [script] :
120
+ noop
121
+ assert 'noop should be true'
122
+ EXAMPLES
123
+ }
124
+ end
125
+
126
+ end
data/lib/test_file.rb ADDED
@@ -0,0 +1,59 @@
1
+ #
2
+ # A single Test File, might have multiple tests.
3
+ #
4
+ class TestFile
5
+
6
+ #
7
+ # Set up the test file.
8
+ #
9
+ def initialize( engine, path_name )
10
+ @engine = engine
11
+ @path_name = path_name
12
+
13
+ @tests = []
14
+ end
15
+
16
+ #
17
+ # Execute all tests in this file and add results
18
+ # to the overall test results.
19
+ #
20
+ def run_tests( results )
21
+ # Load the test file
22
+ @engine.persist_man.load( @path_name )
23
+ # @engine.parser.run '.'
24
+
25
+ # Find all Test objects in the loaded file(s)
26
+ # The loaded file might have loaded other files
27
+ @tests = Gloo::Core::ObjFinder.by_type( @engine, 'test' )
28
+
29
+ # randomize the tests
30
+ @tests.shuffle!
31
+
32
+ # For each Test object, run its on_test script
33
+ @tests.each do |o|
34
+ result = o.run_test
35
+ results.add_result result
36
+ end
37
+
38
+ # Collect results from each test
39
+
40
+ # Aggregate result from test into overall results
41
+
42
+ # file.show_info
43
+ end
44
+
45
+ #
46
+ # Get the number of tests run.
47
+ #
48
+ def tests_run_count
49
+ return @tests.length
50
+ end
51
+
52
+ def show_info
53
+ puts "Test file: #{@path_name}"
54
+ end
55
+
56
+ end
57
+
58
+
59
+
data/lib/test_files.rb ADDED
@@ -0,0 +1,102 @@
1
+ #
2
+ # The collection of Test Files.
3
+ #
4
+ class TestFiles
5
+ include Enumerable
6
+
7
+ TEST_FILE_PATTERN = '**/*.test.gloo'
8
+
9
+ #
10
+ # Set up the test file.
11
+ #
12
+ def initialize( engine, input_files = nil )
13
+ @engine = engine
14
+ @input_files = input_files
15
+ @files = []
16
+ end
17
+
18
+ #
19
+ # Detect test files.
20
+ #
21
+ def detect_files
22
+ @engine.log.debug "Detecting test files…"
23
+
24
+ if @input_files.length > 0
25
+ use_input_files
26
+ else
27
+ # Use the current directory
28
+ look_for_files_in Dir.pwd
29
+ end
30
+
31
+ @engine.log.debug "Found #{count} test files"
32
+ end
33
+
34
+ #
35
+ # Use the files and or directories specified by the user.
36
+ #
37
+ def use_input_files
38
+ @engine.log.debug "Input files specified: #{@input_files}"
39
+ @input_files.each do |f|
40
+ @engine.log.debug "Test file specified: #{f}"
41
+ if Dir.exist?( f )
42
+ # Expand path for file
43
+ f = File.expand_path( f )
44
+ look_for_files_in f
45
+ elsif File.exist?( f )
46
+ add( f )
47
+ else
48
+ # TODO: Show error
49
+ puts "Test file does not exist: #{f}"
50
+ end
51
+ end
52
+ end
53
+
54
+ #
55
+ # Look for test files in a directory.
56
+ # The directory might be provided as a parameter to the test runner,
57
+ # or if no parameter is provided, it is the current directory.
58
+ #
59
+ def look_for_files_in dir
60
+ @engine.log.debug "Looking for test files in: #{dir}"
61
+ root = File.join( dir, TEST_FILE_PATTERN )
62
+ files = Dir.glob( root )
63
+ @engine.log.debug "Found files: #{@files}"
64
+ files.each do |f|
65
+ @engine.log.debug "Found: #{f}"
66
+ add f
67
+ end
68
+ end
69
+
70
+ #
71
+ # Add a test file to the collection.
72
+ #
73
+ def add( file )
74
+ @files << TestFile.new( @engine, file )
75
+ end
76
+
77
+ #
78
+ # Randomize the order of the test files.
79
+ #
80
+ def randomize
81
+ @engine.log.debug "Randomizing test files…"
82
+ @files.shuffle!
83
+ end
84
+
85
+ #
86
+ # Get the number of test files.
87
+ #
88
+ def count
89
+ return @files.length
90
+ end
91
+
92
+ #
93
+ # Iterator method for Enumerable interface.
94
+ #
95
+ def each(&block)
96
+ @files.each(&block)
97
+ end
98
+
99
+ end
100
+
101
+
102
+
@@ -0,0 +1,74 @@
1
+ #
2
+ # Test Runner.
3
+ #
4
+ class TestRunner
5
+
6
+
7
+ #
8
+ # Set up the test runner.
9
+ #
10
+ def initialize( engine, input_files = nil )
11
+ @engine = engine
12
+
13
+ @files = TestFiles.new( @engine, input_files )
14
+ @results = Results.new( @engine )
15
+
16
+ @engine.log.debug "TestRunner initialized"
17
+ end
18
+
19
+ #
20
+ # Execute all tests and display results.
21
+ #
22
+ def run
23
+ setup
24
+
25
+ @results.start_timer
26
+ run_for_files
27
+ @results.end_timer
28
+
29
+ @results.show_failures
30
+ @results.show_results
31
+ @engine.log.debug "TestRunner is finished"
32
+ end
33
+
34
+ #
35
+ # Set up the test runner.
36
+ #
37
+ def setup
38
+ @engine.log.debug "TestRunner is running…"
39
+ puts
40
+ puts "Gloo Test Runner".white
41
+ puts
42
+
43
+ # Get a list of test files and randomize
44
+ @files.detect_files
45
+ @results.file_count = @files.count
46
+ @files.randomize
47
+ end
48
+
49
+ #
50
+ # Run tests for each file.
51
+ #
52
+ def run_for_files
53
+ @files.each do |file|
54
+ run_one_file( file )
55
+ end
56
+ puts
57
+ end
58
+
59
+ #
60
+ # Run tests for a single file.
61
+ # Once done, reset the engine's state.
62
+ #
63
+ def run_one_file( file )
64
+ # Run all tests in the file
65
+ # Add the results to the overall results
66
+ file.run_tests( @results )
67
+
68
+ # Reset the engine's state
69
+ @engine.reset_state
70
+ end
71
+ end
72
+
73
+
74
+
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gloo-test
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.1'
5
+ platform: ruby
6
+ authors:
7
+ - Eric Crane
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Adds Gloo Unit Test support to Gloo.
13
+ email:
14
+ - eric.crane@mac.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - README.md
20
+ - lib/assert.rb
21
+ - lib/gloo-test.rb
22
+ - lib/refute.rb
23
+ - lib/result.rb
24
+ - lib/results.rb
25
+ - lib/test.rb
26
+ - lib/test_file.rb
27
+ - lib/test_files.rb
28
+ - lib/test_runner.rb
29
+ homepage: https://github.com/ecrane/gloo
30
+ licenses:
31
+ - MIT
32
+ metadata:
33
+ gloo.type: core-library
34
+ documentation_uri: https://github.com/ecrane/gloo
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubygems_version: 4.0.17
50
+ specification_version: 4
51
+ summary: Gloo core library. Gloo Unit Test support.
52
+ test_files: []