omnitest-skeptic 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +5 -0
  5. data/.rubocop_todo.yml +36 -0
  6. data/Gemfile +24 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +31 -0
  9. data/Rakefile +12 -0
  10. data/bin/skeptic +4 -0
  11. data/lib/omnitest/skeptic.rb +123 -0
  12. data/lib/omnitest/skeptic/cli.rb +156 -0
  13. data/lib/omnitest/skeptic/configuration.rb +48 -0
  14. data/lib/omnitest/skeptic/errors.rb +19 -0
  15. data/lib/omnitest/skeptic/evidence.rb +81 -0
  16. data/lib/omnitest/skeptic/property_definition.rb +8 -0
  17. data/lib/omnitest/skeptic/result.rb +27 -0
  18. data/lib/omnitest/skeptic/scenario.rb +167 -0
  19. data/lib/omnitest/skeptic/scenario_definition.rb +41 -0
  20. data/lib/omnitest/skeptic/spies.rb +43 -0
  21. data/lib/omnitest/skeptic/spy.rb +23 -0
  22. data/lib/omnitest/skeptic/test_manifest.rb +78 -0
  23. data/lib/omnitest/skeptic/test_statuses.rb +63 -0
  24. data/lib/omnitest/skeptic/test_transitions.rb +172 -0
  25. data/lib/omnitest/skeptic/validation.rb +39 -0
  26. data/lib/omnitest/skeptic/validator.rb +34 -0
  27. data/lib/omnitest/skeptic/validator_registry.rb +33 -0
  28. data/lib/omnitest/skeptic/version.rb +5 -0
  29. data/omnitest-skeptic.gemspec +34 -0
  30. data/spec/fabricators/psychic_fabricator.rb +12 -0
  31. data/spec/fabricators/scenario_fabricator.rb +6 -0
  32. data/spec/fabricators/validator_fabricator.rb +12 -0
  33. data/spec/fixtures/factorial.py +18 -0
  34. data/spec/fixtures/skeptic.yaml +16 -0
  35. data/spec/omnitest/skeptic/evidence_spec.rb +58 -0
  36. data/spec/omnitest/skeptic/result_spec.rb +51 -0
  37. data/spec/omnitest/skeptic/scenario_definition_spec.rb +39 -0
  38. data/spec/omnitest/skeptic/scenario_spec.rb +35 -0
  39. data/spec/omnitest/skeptic/test_manifest_spec.rb +28 -0
  40. data/spec/omnitest/skeptic/validator_registry_spec.rb +40 -0
  41. data/spec/omnitest/skeptic/validator_spec.rb +70 -0
  42. data/spec/omnitest/skeptic_spec.rb +65 -0
  43. data/spec/spec_helper.rb +65 -0
  44. metadata +289 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b9b6bff210ea41042bae9066f417f98d590d3cea
4
+ data.tar.gz: 58655d93652db3a0005df71549e61e5df3b10546
5
+ SHA512:
6
+ metadata.gz: 234205a3ed225713b2b5673167762647c1b515e48238768188004512acd1a18b81cec031e7faf51d7b2ad7abfdc547baf6a710189f63d9aa06ae212ed4780557
7
+ data.tar.gz: a904457a02e2ba152e4ae663230e1a2b831165d62148092b58199f21b79720a2ca89d50d858e7a8af63e995bdea0ff64e01a6e222172fb8812d69f1ff430ab07
@@ -0,0 +1,6 @@
1
+ Gemfile.lock
2
+ tmp/
3
+ coverage/
4
+ site/
5
+ .omnitest/
6
+ reports/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,5 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ Exclude:
5
+ - 'tmp/**/*'
@@ -0,0 +1,36 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2015-01-29 15:35:53 -0500 using RuboCop version 0.27.0.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 1
9
+ Lint/UselessAssignment:
10
+ Enabled: false
11
+
12
+ # Offense count: 7
13
+ Metrics/AbcSize:
14
+ Max: 28
15
+
16
+ # Offense count: 2
17
+ Metrics/CyclomaticComplexity:
18
+ Max: 12
19
+
20
+ # Offense count: 88
21
+ # Configuration parameters: AllowURI, URISchemes.
22
+ Metrics/LineLength:
23
+ Max: 132
24
+
25
+ # Offense count: 9
26
+ # Configuration parameters: CountComments.
27
+ Metrics/MethodLength:
28
+ Max: 20
29
+
30
+ # Offense count: 30
31
+ Style/Documentation:
32
+ Enabled: false
33
+
34
+ # Offense count: 2
35
+ Style/RegexpLiteral:
36
+ MaxSlashes: 0
data/Gemfile ADDED
@@ -0,0 +1,24 @@
1
+ source 'https://rubygems.org'
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ # This hack inspired by rspec...
8
+ branch = begin
9
+ File.read(File.expand_path('../maintenance-branch', __FILE__)).chomp
10
+ rescue
11
+ 'working'
12
+ end
13
+
14
+ %w(omnitest-core psychic).each do |lib|
15
+ library_path = File.expand_path("../../#{lib}", __FILE__)
16
+ gem_name = lib.start_with?('omnitest') ? lib : "omnitest-#{lib}"
17
+ if File.exist?(library_path) && !ENV['USE_GIT_REPOS']
18
+ gem gem_name, path: library_path
19
+ else
20
+ gem gem_name, git: "git://github.com/omnitest/#{lib}.git", branch: branch
21
+ end
22
+ end
23
+
24
+ gem 'pry'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Max Lincoln
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ # Psychic::Skeptic
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'psychic-skeptic'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install psychic-skeptic
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/psychic-skeptic/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rubocop/rake_task'
3
+ require 'rake/notes/rake_task'
4
+ require 'rspec/core/rake_task'
5
+
6
+ task default: [:spec, :rubocop, :notes]
7
+
8
+ RSpec::Core::RakeTask.new('spec')
9
+ RuboCop::RakeTask.new(:rubocop) do |task|
10
+ # abort rake on failure
11
+ task.fail_on_error = true
12
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'omnitest/skeptic/cli'
3
+
4
+ Omnitest::Skeptic::CLI.start
@@ -0,0 +1,123 @@
1
+ require 'rspec/expectations' # exceptions are being stored as classes, so this is needed to load
2
+ require 'omnitest/core'
3
+ require 'omnitest/psychic'
4
+ require 'omnitest/skeptic/version'
5
+ require 'omnitest/skeptic/errors'
6
+
7
+ module Omnitest
8
+ class Skeptic
9
+ autoload :Configuration, 'omnitest/skeptic/configuration'
10
+ autoload :ScenarioDefinition, 'omnitest/skeptic/scenario_definition'
11
+ autoload :PropertyDefinition, 'omnitest/skeptic/property_definition'
12
+ autoload :Scenario, 'omnitest/skeptic/scenario'
13
+ autoload :TestStatuses, 'omnitest/skeptic/test_statuses'
14
+ autoload :TestTransitions, 'omnitest/skeptic/test_transitions'
15
+ autoload :TestManifest, 'omnitest/skeptic/test_manifest'
16
+ autoload :Evidence, 'omnitest/skeptic/evidence'
17
+ autoload :Result, 'omnitest/skeptic/result'
18
+ autoload :Spy, 'omnitest/skeptic/spy'
19
+ autoload :Spies, 'omnitest/skeptic/spies'
20
+ autoload :Validation, 'omnitest/skeptic/validation'
21
+ autoload :Validator, 'omnitest/skeptic/validator'
22
+ autoload :ValidatorRegistry, 'omnitest/skeptic/validator_registry'
23
+
24
+ class << self
25
+ include Core::Configurable
26
+
27
+ def acts_on_scenario(action)
28
+ define_method action do | regex = 'all', options = {} |
29
+ scenarios(regex, options).each do | scenario |
30
+ scenario.public_send(action)
31
+ end
32
+ end
33
+ end
34
+
35
+ def acts_on_scenario_with_options(action)
36
+ define_method action do | regex = 'all', options = {} |
37
+ scenarios(regex, options).each do | scenario |
38
+ scenario.public_send(action, options)
39
+ end
40
+ end
41
+ end
42
+
43
+ # Registers a {Omnitest::Skeptic::Validator} that will be used during test
44
+ # execution on matching {Omnitest::Skeptic::Scenario}s.
45
+ def validate(desc, scope = { suite: //, scenario: // }, &block)
46
+ fail ArgumentError, 'You must pass block' unless block_given?
47
+ validator = Omnitest::Skeptic::Validator.new(desc, scope, &block)
48
+
49
+ Omnitest::Skeptic::ValidatorRegistry.register validator
50
+ validator
51
+ end
52
+ end
53
+
54
+ def initialize(psychic = Psychic.new)
55
+ psychic = Psychic.new(psychic) if psychic.is_a? Hash
56
+ @psychic = psychic
57
+ end
58
+
59
+ def manifest
60
+ Skeptic.configuration.manifest
61
+ end
62
+
63
+ def scenario_definitions
64
+ manifest.scenario_definitions
65
+ end
66
+
67
+ def build_scenarios
68
+ scenario_definitions.map do | scenario_definition |
69
+ scenario_definition.build @psychic
70
+ end
71
+ end
72
+
73
+ def scenario(name)
74
+ scenarios.find { |s| s.name == name }
75
+ end
76
+
77
+ def all_scenarios
78
+ @scenarios ||= build_scenarios
79
+ end
80
+
81
+ def select_scenarios(regexp)
82
+ regexp ||= 'all'
83
+ if regexp == 'all'
84
+ return all_scenarios
85
+ else
86
+ selected_scenarios = all_scenarios.find { |c| c.full_name == regexp } ||
87
+ all_scenarios.select { |c| c.full_name =~ /#{regexp}/i }
88
+ end
89
+
90
+ if selected_scenarios.is_a? Array
91
+ selected_scenarios
92
+ else
93
+ [selected_scenarios]
94
+ end
95
+ end
96
+
97
+ def scenarios(regexp = 'all', options = {})
98
+ selected_scenarios = select_scenarios regexp
99
+ selected_scenarios.keep_if { |scenario| scenario.failed? == options[:failed] } unless options[:failed].nil?
100
+ selected_scenarios.keep_if { |scenario| scenario.skipped? == options[:skipped] } unless options[:skipped].nil?
101
+ selected_scenarios.keep_if { |scenario| scenario.sample? == options[:samples] } unless options[:samples].nil?
102
+ selected_scenarios
103
+ end
104
+
105
+ def summary
106
+ summary_data = ["#{scenarios.size} scenarios"]
107
+ scenarios.group_by(&:status).each do | _status, group |
108
+ # Note: Removes partially verified's parenthetical description
109
+ status_description = group.first.status_description.gsub(/\(.*/, '')
110
+ summary_data << "#{group.size} #{status_description.downcase}"
111
+ end
112
+ summary_data.join("\n ")
113
+ end
114
+
115
+ acts_on_scenario_with_options :code2doc
116
+ acts_on_scenario :test
117
+ Scenario::FSM::TRANSITIONS.each do | transition |
118
+ acts_on_scenario transition
119
+ end
120
+ end
121
+ end
122
+
123
+ Omnitest.mutex = Mutex.new
@@ -0,0 +1,156 @@
1
+ require 'omnitest/skeptic'
2
+
3
+ module Omnitest
4
+ class Skeptic
5
+ class BaseCLI < Omnitest::Core::CLI
6
+ attr_accessor :psychic, :skeptic
7
+
8
+ no_commands do
9
+ def update_config!
10
+ Omnitest::Skeptic.configuration.manifest_file = options[:skeptic]
11
+ autogenerate_manifest if options[:glob]
12
+ runner_opts = { cwd: Dir.pwd, cli: shell, parameters: options.parameters }
13
+ runner_opts.merge!(Omnitest::Core::Util.symbolized_hash(options))
14
+ @psychic = Omnitest::Psychic.new(runner_opts)
15
+ @skeptic = Omnitest::Skeptic.new(@psychic)
16
+ end
17
+
18
+ def autogenerate_manifest
19
+ data = { suites: {} }
20
+ suites = Dir[*options[:glob]].group_by do | file |
21
+ Pathname(file).dirname.to_s
22
+ end
23
+ suites.each do | suite, files |
24
+ data[:suites][suite] = {}
25
+ data[:suites][suite][:samples] = files
26
+ end
27
+ Omnitest::Skeptic.configuration.manifest = TestManifest.new(data)
28
+ end
29
+ end
30
+ end
31
+
32
+ class CLI < BaseCLI # rubocop:disable Metrics/ClassLength
33
+ # The maximum number of concurrent instances that can run--which is a bit
34
+ # high
35
+ MAX_CONCURRENCY = 9999
36
+
37
+ desc 'code2doc [SCENARIO|REGEXP|all]',
38
+ 'Convert scripts for code samples to lightweight documentation formats'
39
+ method_option :skeptic,
40
+ aliases: '-s',
41
+ desc: 'The Skeptic test manifest file',
42
+ default: 'skeptic.yaml'
43
+ method_option :test_dir,
44
+ aliases: '-t',
45
+ desc: 'The Omnitest test directory',
46
+ default: 'tests/omnitest'
47
+ method_option :glob,
48
+ type: :array,
49
+ aliases: '-g',
50
+ desc: 'Automatically build scenarios for samples matching the glob pattern(s)',
51
+ lazy_default: true
52
+ method_option :format,
53
+ enum: %w(md rst),
54
+ default: 'md',
55
+ desc: 'Target documentation format'
56
+ method_option :destination,
57
+ aliases: '-d',
58
+ default: 'docs/',
59
+ desc: 'The target directory where documentation for generated documentation.'
60
+ method_option :glob,
61
+ type: :array,
62
+ aliases: '-g',
63
+ desc: 'Automatically build scenarios for samples matching the glob pattern(s)'
64
+ def code2doc(regex = 'all')
65
+ update_config!
66
+ action_options = options.dup
67
+ skeptic.public_send(:code2doc, regex, action_options)
68
+ end
69
+
70
+ desc 'test [SCENARIO|REGEXP|all]',
71
+ 'Test (clone, bootstrap, exec, and verify) one or more scenarios'
72
+ long_desc <<-DESC
73
+ The scenario states are in order: cloned, bootstrapped, executed, verified.
74
+ Test changes the state of one or more scenarios executes
75
+ the actions for each state up to verify.
76
+ DESC
77
+ method_option :concurrency,
78
+ aliases: '-c',
79
+ type: :numeric,
80
+ lazy_default: MAX_CONCURRENCY,
81
+ desc: <<-DESC.gsub(/^\s+/, '').gsub(/\n/, ' ')
82
+ Run a test against all matching instances concurrently. Only N
83
+ instances will run at the same time if a number is given.
84
+ DESC
85
+ method_option :log_level,
86
+ aliases: '-l',
87
+ desc: 'Set the log level (debug, info, warn, error, fatal)'
88
+ method_option :skeptic,
89
+ aliases: '-s',
90
+ desc: 'The Skeptic test manifest file',
91
+ default: 'skeptic.yaml'
92
+ method_option :test_dir,
93
+ aliases: '-t',
94
+ desc: 'The Omnitest test directory',
95
+ default: 'tests/omnitest'
96
+ method_option :glob,
97
+ type: :array,
98
+ aliases: '-g',
99
+ desc: 'Automatically build scenarios for samples matching the glob pattern(s)'
100
+ def test(regex = 'all')
101
+ update_config!
102
+ action_options = options.dup
103
+ skeptic.public_send(:test, regex, action_options)
104
+ end
105
+
106
+ {
107
+ detect: 'Find sample code that matches a test scenario. ' \
108
+ 'Attempts to locate a code sample with a filename that the test scenario name.',
109
+ exec: 'Change instance state to executed. ' \
110
+ 'Execute the code sample and capture the results.',
111
+ verify: 'Change instance state to verified. ' \
112
+ 'Assert that the captured results match the expectations for the scenario.',
113
+ clear: 'Clear stored results for the scenario. ' \
114
+ 'Delete all stored results for one or more scenarios'
115
+ }.each do |action, short_desc|
116
+ desc(
117
+ "#{action} [SCENARIO|REGEXP|all]",
118
+ short_desc
119
+ )
120
+ long_desc <<-DESC
121
+ The scenario states are in order: cloned, bootstrapped, executed, verified.
122
+ Change one or more scenarios from the current state to the #{action} state. Actions for all
123
+ intermediate states will be executed.
124
+ DESC
125
+ method_option :concurrency,
126
+ aliases: '-c',
127
+ type: :numeric,
128
+ lazy_default: MAX_CONCURRENCY,
129
+ desc: <<-DESC.gsub(/^\s+/, '').gsub(/\n/, ' ')
130
+ Run a #{action} against all matching instances concurrently. Only N
131
+ instances will run at the same time if a number is given.
132
+ DESC
133
+ method_option :log_level,
134
+ aliases: '-l',
135
+ desc: 'Set the log level (debug, info, warn, error, fatal)'
136
+ method_option :file,
137
+ aliases: '-f',
138
+ desc: 'The Omnitest project set file',
139
+ default: 'omnitest.yaml'
140
+ method_option :skeptic,
141
+ aliases: '-s',
142
+ desc: 'The Skeptic test manifest file',
143
+ default: 'skeptic.yaml'
144
+ method_option :test_dir,
145
+ aliases: '-t',
146
+ desc: 'The Omnitest test directory',
147
+ default: 'tests/omnitest'
148
+ define_method(action) do |regex = 'all'|
149
+ update_config!
150
+ action_options = options.dup
151
+ skeptic.public_send(action, regex, action_options)
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,48 @@
1
+ module Omnitest
2
+ class Skeptic
3
+ class Configuration < Omnitest::Core::Dash
4
+ field :seed, Integer, default: Process.pid # or Random.new_seed
5
+ field :manifest, TestManifest
6
+ field :manifest_file, Pathname, default: 'skeptic.yaml'
7
+
8
+ def manifest
9
+ self[:manifest] ||= load_manifest
10
+ end
11
+
12
+ def manifest_file=(file)
13
+ self[:manifest] = nil
14
+ self[:manifest_file] = file
15
+ end
16
+
17
+ def load_manifest
18
+ ENV['SKEPTIC_SEED'] = seed.to_s
19
+ Skeptic::TestManifest.from_yaml manifest_file
20
+ rescue Errno::ENOENT => e
21
+ raise UserError, "Could not load test manifest: #{e.message}"
22
+ end
23
+
24
+ # The callback used to validate code samples that
25
+ # don't have a custom validator. The default
26
+ # checks that the sample code runs successfully.
27
+ def default_validator_callback
28
+ @default_validator_callback ||= proc do |scenario|
29
+ expect(scenario.result.execution_result.exitstatus).to eq(0)
30
+ end
31
+ end
32
+
33
+ def default_validator
34
+ @default_validator ||= Skeptic::Validator.new('default validator', suite: //, scenario: //, &default_validator_callback)
35
+ end
36
+
37
+ attr_writer :default_validator_callback
38
+
39
+ def register_spy(spy)
40
+ Omnitest::Skeptic::Spies.register_spy(spy)
41
+ end
42
+
43
+ def clear
44
+ ValidatorRegistry.clear
45
+ end
46
+ end
47
+ end
48
+ end