omnitest-core 0.0.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 +7 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +30 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +4 -0
- data/Rakefile +12 -0
- data/lib/omnitest/core.rb +56 -0
- data/lib/omnitest/core/cli.rb +70 -0
- data/lib/omnitest/core/color.rb +42 -0
- data/lib/omnitest/core/configurable.rb +21 -0
- data/lib/omnitest/core/file_system.rb +60 -0
- data/lib/omnitest/core/hashie.rb +51 -0
- data/lib/omnitest/core/logger.rb +42 -0
- data/lib/omnitest/core/logging.rb +133 -0
- data/lib/omnitest/core/util.rb +204 -0
- data/lib/omnitest/core/version.rb +5 -0
- data/lib/omnitest/errors.rb +244 -0
- data/omnitest-core.gemspec +32 -0
- metadata +237 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bdb34e38c505f97708ebeea946cf17c9fd6c4c8e
|
4
|
+
data.tar.gz: 57c68bab3e68e34e8e11d28778521b0f168cafe4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 57c9056f0e7b5dc6d09404c8ebc019989b415b998d6015850fe798cdf3596188781a5c5bba8d93f6d311914ee787efe526d11716809fc3ae5bcce8d536f95b85
|
7
|
+
data.tar.gz: f6accd7f908e093e1426af222caf8a153e6474be2abe4abf8a0e9fe32cc6dcbc4bcf043352249c86b67ed8299653200414755772658052a2089f0ad08ced2c9f
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
+
# on 2015-01-20 16:57:55 -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
|
+
Metrics/AbcSize:
|
10
|
+
Max: 18
|
11
|
+
|
12
|
+
# Offense count: 10
|
13
|
+
# Configuration parameters: AllowURI, URISchemes.
|
14
|
+
Metrics/LineLength:
|
15
|
+
Max: 105
|
16
|
+
|
17
|
+
# Offense count: 4
|
18
|
+
# Configuration parameters: CountComments.
|
19
|
+
Metrics/MethodLength:
|
20
|
+
Max: 13
|
21
|
+
|
22
|
+
# Offense count: 14
|
23
|
+
Style/Documentation:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
# Offense count: 2
|
27
|
+
# Configuration parameters: MaxSlashes.
|
28
|
+
Style/RegexpLiteral:
|
29
|
+
Enabled: false
|
30
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 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.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -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,56 @@
|
|
1
|
+
require 'omnitest/core/version'
|
2
|
+
require 'logger'
|
3
|
+
require 'cause'
|
4
|
+
require 'thor'
|
5
|
+
require 'pathname'
|
6
|
+
require 'omnitest/errors'
|
7
|
+
|
8
|
+
module Omnitest
|
9
|
+
module Core
|
10
|
+
autoload :Configurable, 'omnitest/core/configurable'
|
11
|
+
autoload :Util, 'omnitest/core/util'
|
12
|
+
autoload :FileSystem, 'omnitest/core/file_system'
|
13
|
+
autoload :CLI, 'omnitest/core/cli'
|
14
|
+
autoload :Logger, 'omnitest/core/logger'
|
15
|
+
autoload :Logging, 'omnitest/core/logging'
|
16
|
+
autoload :DefaultLogger, 'omnitest/core/logging'
|
17
|
+
autoload :StdoutLogger, 'omnitest/core/logging'
|
18
|
+
autoload :LogdevLogger, 'omnitest/core/logging'
|
19
|
+
autoload :Color, 'omnitest/core/color'
|
20
|
+
autoload :Dash, 'omnitest/core/hashie'
|
21
|
+
autoload :Mash, 'omnitest/core/hashie'
|
22
|
+
end
|
23
|
+
|
24
|
+
include Omnitest::Core::Logger
|
25
|
+
include Omnitest::Core::Logging
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# @return [Logger] the common Omnitest logger
|
29
|
+
attr_accessor :logger
|
30
|
+
|
31
|
+
# @return [Mutex] a common mutex for global coordination
|
32
|
+
attr_accessor :mutex
|
33
|
+
|
34
|
+
def basedir
|
35
|
+
@basedir ||= Dir.pwd
|
36
|
+
end
|
37
|
+
|
38
|
+
def logger
|
39
|
+
@logger ||= Core::StdoutLogger.new($stdout)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Determine the default log level from an environment variable, if it is
|
43
|
+
# set.
|
44
|
+
#
|
45
|
+
# @return [Integer,nil] a log level or nil if not set
|
46
|
+
# @api private
|
47
|
+
def env_log
|
48
|
+
level = ENV['CROSSTEST_LOG'] && ENV['CROSSTEST_LOG'].downcase.to_sym
|
49
|
+
level = Util.to_logger_level(level) unless level.nil?
|
50
|
+
level
|
51
|
+
end
|
52
|
+
|
53
|
+
# Default log level verbosity
|
54
|
+
DEFAULT_LOG_LEVEL = :info
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'English'
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
module Omnitest
|
5
|
+
module Core
|
6
|
+
class CLI < Thor
|
7
|
+
# Common module to load and invoke a CLI-implementation agnostic command.
|
8
|
+
module PerformCommand
|
9
|
+
attr_reader :action
|
10
|
+
|
11
|
+
# Perform a scenario subcommand.
|
12
|
+
#
|
13
|
+
# @param task [String] action to take, usually corresponding to the
|
14
|
+
# subcommand name
|
15
|
+
# @param command [String] command class to create and invoke]
|
16
|
+
# @param args [Array] remainder arguments from processed ARGV
|
17
|
+
# (default: `nil`)
|
18
|
+
# @param additional_options [Hash] additional configuration needed to
|
19
|
+
# set up the command class (default: `{}`)
|
20
|
+
def perform(task, command, args = nil, additional_options = {})
|
21
|
+
require "omnitest/command/#{command}"
|
22
|
+
|
23
|
+
command_options = {
|
24
|
+
help: -> { help(task) },
|
25
|
+
test_dir: @test_dir,
|
26
|
+
shell: shell
|
27
|
+
}.merge(additional_options)
|
28
|
+
|
29
|
+
str_const = Thor::Util.camel_case(command)
|
30
|
+
klass = ::Omnitest::Command.const_get(str_const)
|
31
|
+
klass.new(task, args, options, command_options).call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
include Core::Logging
|
36
|
+
include PerformCommand
|
37
|
+
|
38
|
+
class << self
|
39
|
+
# Override Thor's start to strip extra_args from ARGV before it's processed
|
40
|
+
attr_accessor :extra_args
|
41
|
+
|
42
|
+
def start(given_args = ARGV, config = {})
|
43
|
+
if given_args && (split_pos = given_args.index('--'))
|
44
|
+
@extra_args = given_args.slice(split_pos + 1, given_args.length).map do | arg |
|
45
|
+
# Restore quotes
|
46
|
+
arg.match(/\=/) ? restore_quotes(arg) : arg
|
47
|
+
end
|
48
|
+
given_args = given_args.slice(0, split_pos)
|
49
|
+
end
|
50
|
+
super given_args, config
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def restore_quotes(arg)
|
56
|
+
lhs, rhs = arg.split('=')
|
57
|
+
lhs = "\"#{lhs}\"" if lhs.match(/\s/)
|
58
|
+
rhs = "\"#{rhs}\"" if rhs.match(/\s/)
|
59
|
+
[lhs, rhs].join('=')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
no_commands do
|
64
|
+
def extra_args
|
65
|
+
self.class.extra_args
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Omnitest
|
2
|
+
module Core
|
3
|
+
module Color
|
4
|
+
ANSI = {
|
5
|
+
reset: 0, black: 30, red: 31, green: 32, yellow: 33,
|
6
|
+
blue: 34, magenta: 35, cyan: 36, white: 37,
|
7
|
+
bright_black: 90, bright_red: 91, bright_green: 92,
|
8
|
+
bright_yellow: 93, bright_blue: 94, bright_magenta: 95,
|
9
|
+
bright_cyan: 96, bright_white: 97
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
COLORS = %w(
|
13
|
+
cyan yellow green magenta blue bright_cyan bright_yellow
|
14
|
+
bright_green bright_magenta bright_blue
|
15
|
+
).freeze
|
16
|
+
|
17
|
+
# Returns an ansi escaped string representing a color control sequence.
|
18
|
+
#
|
19
|
+
# @param name [Symbol] a valid color representation, taken from
|
20
|
+
# Omnitest::Color::ANSI
|
21
|
+
# @return [String] an ansi escaped string if the color is valid and an
|
22
|
+
# empty string otherwise
|
23
|
+
def self.escape(name)
|
24
|
+
return '' if name.nil?
|
25
|
+
return '' unless ANSI[name]
|
26
|
+
"\e[#{ANSI[name]}m"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns a colorized ansi escaped string with the given color.
|
30
|
+
#
|
31
|
+
# @param str [String] a string to colorize
|
32
|
+
# @param name [Symbol] a valid color representation, taken from
|
33
|
+
# Omnitest::Color::ANSI
|
34
|
+
# @return [String] an ansi escaped string if the color is valid and an
|
35
|
+
# unescaped string otherwise
|
36
|
+
def self.colorize(str, name)
|
37
|
+
color = escape(name)
|
38
|
+
color.empty? ? str : "#{color}#{str}#{escape(:reset)}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Omnitest
|
2
|
+
module Core
|
3
|
+
module Configurable
|
4
|
+
# @see Omnitest::Configuration
|
5
|
+
def configuration
|
6
|
+
fail "configuration doesn't take a block, use configure" if block_given?
|
7
|
+
@configuration ||= const_get('Configuration').new
|
8
|
+
end
|
9
|
+
|
10
|
+
# @see Omnitest::Configuration
|
11
|
+
def configure
|
12
|
+
yield(configuration)
|
13
|
+
end
|
14
|
+
|
15
|
+
def reset
|
16
|
+
configuration.clear
|
17
|
+
@configuration = nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Omnitest
|
2
|
+
module Core
|
3
|
+
module FileSystem
|
4
|
+
include Util::String
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# Finds a file by loosely matching the file name to a scenario name
|
8
|
+
def find_file(search_path, scenario_name, ignored_patterns = nil)
|
9
|
+
ignored_patterns ||= read_gitignore(search_path)
|
10
|
+
glob_string = "#{search_path}/**/*#{slugify(scenario_name)}.*"
|
11
|
+
potential_files = Dir.glob(glob_string, File::FNM_CASEFOLD)
|
12
|
+
potential_files.concat Dir.glob(glob_string.gsub('_', '-'), File::FNM_CASEFOLD)
|
13
|
+
potential_files.concat Dir.glob(glob_string.gsub('_', ''), File::FNM_CASEFOLD)
|
14
|
+
|
15
|
+
# Filter out ignored filesFind the first file, not including generated files
|
16
|
+
files = potential_files.select do |f|
|
17
|
+
!ignored? ignored_patterns, search_path, f
|
18
|
+
end
|
19
|
+
|
20
|
+
# Select the shortest path, likely the best match
|
21
|
+
file = files.min_by(&:length)
|
22
|
+
|
23
|
+
fail Errno::ENOENT, "No file was found for #{scenario_name} within #{search_path}" if file.nil?
|
24
|
+
Pathname.new file
|
25
|
+
end
|
26
|
+
|
27
|
+
def relativize(file, base_path)
|
28
|
+
absolute_file = File.absolute_path(file)
|
29
|
+
absolute_base_path = File.absolute_path(base_path)
|
30
|
+
Pathname.new(absolute_file).relative_path_from Pathname.new(absolute_base_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
def read_gitignore(dir)
|
37
|
+
gitignore_file = "#{dir}/.gitignore"
|
38
|
+
File.read(gitignore_file)
|
39
|
+
rescue
|
40
|
+
''
|
41
|
+
end
|
42
|
+
|
43
|
+
# @api private
|
44
|
+
def ignored?(ignored_patterns, base_path, target_file)
|
45
|
+
# Trying to match the git ignore rules but there's some discrepencies.
|
46
|
+
ignored_patterns.split.find do |pattern|
|
47
|
+
# if git ignores a folder, we should ignore all files it contains
|
48
|
+
pattern = "#{pattern}**" if pattern[-1] == '/'
|
49
|
+
started_with_slash = pattern.start_with? '/'
|
50
|
+
|
51
|
+
pattern.gsub!(/\A\//, '') # remove leading slashes since we're searching from root
|
52
|
+
file = relativize(target_file, base_path)
|
53
|
+
ignored = file.fnmatch? pattern
|
54
|
+
ignored || (file.fnmatch? "**/#{pattern}" unless started_with_slash)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
module Omnitest
|
5
|
+
module Core
|
6
|
+
class Dash < Hashie::Dash
|
7
|
+
include Hashie::Extensions::Coercion
|
8
|
+
|
9
|
+
def initialize(hash = {})
|
10
|
+
super Omnitest::Core::Util.symbolized_hash(hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
# @!macro [attach] field
|
15
|
+
# @!attribute [rw] $1
|
16
|
+
# Attribute $1. $3
|
17
|
+
# @return [$2]
|
18
|
+
# Defines a typed attribute on a class.
|
19
|
+
def self.field(name, type, opts = {})
|
20
|
+
property name, opts
|
21
|
+
coerce_key name, type
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
# @!macro [attach] required_field
|
26
|
+
# @!attribute [rw] $1
|
27
|
+
# **Required** Attribute $1. $3
|
28
|
+
# @return [$2]
|
29
|
+
# Defines a required, typed attribute on a class.
|
30
|
+
def self.required_field(name, type, opts = {})
|
31
|
+
opts[:required] = true
|
32
|
+
field(name, type, opts)
|
33
|
+
end
|
34
|
+
|
35
|
+
module Loadable
|
36
|
+
include Core::DefaultLogger
|
37
|
+
def from_yaml(yaml_file)
|
38
|
+
logger.debug "Loading #{yaml_file}"
|
39
|
+
raw_content = File.read(yaml_file)
|
40
|
+
processed_content = ERB.new(raw_content).result
|
41
|
+
data = YAML.load processed_content
|
42
|
+
new data
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Mash < Hashie::Mash
|
48
|
+
include Hashie::Extensions::Coercion
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Omnitest
|
4
|
+
module Core
|
5
|
+
module Logger
|
6
|
+
def logger
|
7
|
+
@logger ||= new_logger
|
8
|
+
end
|
9
|
+
|
10
|
+
def new_logger(io = $stdout, level = :debug)
|
11
|
+
stdout_logger(io).tap do | logger |
|
12
|
+
logger.level = Util.to_logger_level(level)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Construct a new standard out logger.
|
17
|
+
#
|
18
|
+
# @param stdout [IO] the IO object that represents stdout (or similar)
|
19
|
+
# @param color [Symbol] color to use when outputing messages
|
20
|
+
# @return [StdoutLogger] a new logger
|
21
|
+
# @api private
|
22
|
+
def stdout_logger(stdout, color = nil)
|
23
|
+
logger = Omnitest::Core::StdoutLogger.new(stdout)
|
24
|
+
# if Omnitest.tty?
|
25
|
+
if stdout.tty? && color
|
26
|
+
logger.formatter = proc do |_severity, _datetime, _progname, msg|
|
27
|
+
Core::Color.colorize("#{msg}", color).concat("\n")
|
28
|
+
end
|
29
|
+
else
|
30
|
+
logger.formatter = proc do |_severity, _datetime, _progname, msg|
|
31
|
+
msg.concat("\n")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
logger
|
35
|
+
end
|
36
|
+
|
37
|
+
def log_level=(level)
|
38
|
+
logger.level = level
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Omnitest
|
2
|
+
module Core
|
3
|
+
module DefaultLogger
|
4
|
+
module ClassMethods
|
5
|
+
def logger
|
6
|
+
@logger ||= default_logger
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_logger
|
10
|
+
if Omnitest.respond_to? :configuration
|
11
|
+
Omnitest.configuration.default_logger
|
12
|
+
else
|
13
|
+
::Logger.new(STDOUT)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.included(base)
|
19
|
+
base.extend(ClassMethods)
|
20
|
+
end
|
21
|
+
|
22
|
+
include ClassMethods
|
23
|
+
end
|
24
|
+
|
25
|
+
module Logging
|
26
|
+
class << self
|
27
|
+
private
|
28
|
+
|
29
|
+
def logger_method(meth)
|
30
|
+
define_method(meth) do |*args|
|
31
|
+
logger.public_send(meth, *args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
logger_method :banner
|
37
|
+
logger_method :debug
|
38
|
+
logger_method :info
|
39
|
+
logger_method :warn
|
40
|
+
logger_method :error
|
41
|
+
logger_method :fatal
|
42
|
+
end
|
43
|
+
|
44
|
+
# Internal class which adds a #banner method call that displays the
|
45
|
+
# message with a callout arrow.
|
46
|
+
class LogdevLogger < ::Logger
|
47
|
+
alias_method :super_info, :info
|
48
|
+
|
49
|
+
# Dump one or more messages to info.
|
50
|
+
#
|
51
|
+
# @param msg [String] a message
|
52
|
+
def <<(msg)
|
53
|
+
@buffer ||= ''
|
54
|
+
lines, _, remainder = msg.rpartition("\n")
|
55
|
+
if lines.empty?
|
56
|
+
@buffer << remainder
|
57
|
+
else
|
58
|
+
lines.insert(0, @buffer)
|
59
|
+
lines.split("\n").each { |l| format_line(l.chomp) }
|
60
|
+
@buffer = ''
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Log a banner message.
|
65
|
+
#
|
66
|
+
# @param msg [String] a message
|
67
|
+
def banner(msg = nil, &block)
|
68
|
+
super_info("-----> #{msg}", &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# Reformat a line if it already contains log formatting.
|
74
|
+
#
|
75
|
+
# @param line [String] a message line
|
76
|
+
# @api private
|
77
|
+
def format_line(line)
|
78
|
+
case line
|
79
|
+
when /^-----> / then banner(line.gsub(/^[ >-]{6} /, ''))
|
80
|
+
when /^>>>>>> / then error(line.gsub(/^[ >-]{6} /, ''))
|
81
|
+
when /^ / then info(line.gsub(/^[ >-]{6} /, ''))
|
82
|
+
else info(line)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Internal class which reformats logging methods for display as console
|
88
|
+
# output.
|
89
|
+
class StdoutLogger < LogdevLogger
|
90
|
+
# Log a debug message
|
91
|
+
#
|
92
|
+
# @param msg [String] a message
|
93
|
+
def debug(msg = nil, &block)
|
94
|
+
super("D #{msg}", &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Log an info message
|
98
|
+
#
|
99
|
+
# @param msg [String] a message
|
100
|
+
def info(msg = nil, &block)
|
101
|
+
super(" #{msg}", &block)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Log a warn message
|
105
|
+
#
|
106
|
+
# @param msg [String] a message
|
107
|
+
def warn(msg = nil, &block)
|
108
|
+
super("$$$$$$ #{msg}", &block)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Log an error message
|
112
|
+
#
|
113
|
+
# @param msg [String] a message
|
114
|
+
def error(msg = nil, &block)
|
115
|
+
super(">>>>>> #{msg}", &block)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Log a fatal message
|
119
|
+
#
|
120
|
+
# @param msg [String] a message
|
121
|
+
def fatal(msg = nil, &block)
|
122
|
+
super("!!!!!! #{msg}", &block)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Log an unknown message
|
126
|
+
#
|
127
|
+
# @param msg [String] a message
|
128
|
+
def unknown(msg = nil, &block)
|
129
|
+
super("?????? #{msg}", &block)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Much of this code has been adapted from Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
# work on test-kitchen.
|
5
|
+
|
6
|
+
module Omnitest
|
7
|
+
module Core
|
8
|
+
# Stateless utility methods used in different contexts. Essentially a mini
|
9
|
+
# PassiveSupport library.
|
10
|
+
module Util
|
11
|
+
module Hashable
|
12
|
+
def to_hash
|
13
|
+
instance_variables.each_with_object({}) do |var, hash|
|
14
|
+
hash[var.to_s.delete('@')] = instance_variable_get(var)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the standard library Logger level constants for a given symbol
|
20
|
+
# representation.
|
21
|
+
#
|
22
|
+
# @param symbol [Symbol] symbol representation of a logger level (:debug,
|
23
|
+
# :info, :warn, :error, :fatal)
|
24
|
+
# @return [Integer] Logger::Severity constant value or nil if input is not
|
25
|
+
# valid
|
26
|
+
def self.to_logger_level(symbol)
|
27
|
+
return nil unless [:debug, :info, :warn, :error, :fatal].include?(symbol)
|
28
|
+
|
29
|
+
::Logger.const_get(symbol.to_s.upcase)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the symbol represenation of a logging levels for a given
|
33
|
+
# standard library Logger::Severity constant.
|
34
|
+
#
|
35
|
+
# @param const [Integer] Logger::Severity constant value for a logging
|
36
|
+
# level (Logger::DEBUG, Logger::INFO, Logger::WARN, Logger::ERROR,
|
37
|
+
# Logger::FATAL)
|
38
|
+
# @return [Symbol] symbol representation of the logging level
|
39
|
+
def self.from_logger_level(const)
|
40
|
+
case const
|
41
|
+
when ::Logger::DEBUG then :debug
|
42
|
+
when ::Logger::INFO then :info
|
43
|
+
when ::Logger::WARN then :warn
|
44
|
+
when ::Logger::ERROR then :error
|
45
|
+
else :fatal
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a new Hash with all key values coerced to symbols. All keys
|
50
|
+
# within a Hash are coerced by calling #to_sym and hashes within arrays
|
51
|
+
# and other hashes are traversed.
|
52
|
+
#
|
53
|
+
# @param obj [Object] the hash to be processed. While intended for
|
54
|
+
# hashes, this method safely processes arbitrary objects
|
55
|
+
# @return [Object] a converted hash with all keys as symbols
|
56
|
+
def self.symbolized_hash(obj)
|
57
|
+
if obj.is_a?(Hash)
|
58
|
+
obj.each_with_object({}) do |(k, v), h|
|
59
|
+
h[k.to_sym] = symbolized_hash(v)
|
60
|
+
end
|
61
|
+
elsif obj.is_a?(Array)
|
62
|
+
obj.each_with_object([]) do |e, a|
|
63
|
+
a << symbolized_hash(e)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
obj
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns a new Hash with all key values coerced to strings. All keys
|
71
|
+
# within a Hash are coerced by calling #to_s and hashes with arrays
|
72
|
+
# and other hashes are traversed.
|
73
|
+
#
|
74
|
+
# @param obj [Object] the hash to be processed. While intended for
|
75
|
+
# hashes, this method safely processes arbitrary objects
|
76
|
+
# @return [Object] a converted hash with all keys as strings
|
77
|
+
def self.stringified_hash(obj)
|
78
|
+
if obj.is_a?(Hash)
|
79
|
+
obj.each_with_object({}) do |(k, v), h|
|
80
|
+
h[k.to_s] = stringified_hash(v)
|
81
|
+
end
|
82
|
+
elsif obj.is_a?(Array)
|
83
|
+
obj.each_with_object([]) do |e, a|
|
84
|
+
a << stringified_hash(e)
|
85
|
+
end
|
86
|
+
else
|
87
|
+
obj
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns a formatted string representing a duration in seconds.
|
92
|
+
#
|
93
|
+
# @param total [Integer] the total number of seconds
|
94
|
+
# @return [String] a formatted string of the form (XmYY.00s)
|
95
|
+
def self.duration(total)
|
96
|
+
total = 0 if total.nil?
|
97
|
+
minutes = (total / 60).to_i
|
98
|
+
seconds = (total - (minutes * 60))
|
99
|
+
format('(%dm%.2fs)', minutes, seconds)
|
100
|
+
end
|
101
|
+
|
102
|
+
module String
|
103
|
+
module ClassMethods
|
104
|
+
def slugify(*labels)
|
105
|
+
labels.map do |label|
|
106
|
+
label.downcase.gsub(/[\.\s-]/, '_')
|
107
|
+
end.join('-')
|
108
|
+
end
|
109
|
+
|
110
|
+
def ansi2html(text)
|
111
|
+
HTML.from_ansi(text)
|
112
|
+
end
|
113
|
+
|
114
|
+
def escape_html(text)
|
115
|
+
HTML.escape_html(text)
|
116
|
+
end
|
117
|
+
alias_method :h, :escape_html
|
118
|
+
|
119
|
+
def highlight(source, opts = {})
|
120
|
+
return nil if source.nil?
|
121
|
+
|
122
|
+
opts[:language] ||= 'ruby'
|
123
|
+
opts[:formatter] ||= 'terminal256'
|
124
|
+
Highlight.new(opts).highlight(source)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.included(base)
|
129
|
+
base.extend(ClassMethods)
|
130
|
+
end
|
131
|
+
|
132
|
+
include ClassMethods
|
133
|
+
end
|
134
|
+
|
135
|
+
class Highlight
|
136
|
+
def initialize(opts)
|
137
|
+
@lexer = Rouge::Lexer.find(opts[:language]) || Rouge::Lexer.guess_by_filename(opts[:filename])
|
138
|
+
@formatter = opts[:formatter]
|
139
|
+
end
|
140
|
+
|
141
|
+
def highlight(source)
|
142
|
+
Rouge.highlight(source, @lexer, @formatter)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class HTML
|
147
|
+
ANSICODES = {
|
148
|
+
'1' => 'bold',
|
149
|
+
'4' => 'underline',
|
150
|
+
'30' => 'black',
|
151
|
+
'31' => 'red',
|
152
|
+
'32' => 'green',
|
153
|
+
'33' => 'yellow',
|
154
|
+
'34' => 'blue',
|
155
|
+
'35' => 'magenta',
|
156
|
+
'36' => 'cyan',
|
157
|
+
'37' => 'white',
|
158
|
+
'40' => 'bg-black',
|
159
|
+
'41' => 'bg-red',
|
160
|
+
'42' => 'bg-green',
|
161
|
+
'43' => 'bg-yellow',
|
162
|
+
'44' => 'bg-blue',
|
163
|
+
'45' => 'bg-magenta',
|
164
|
+
'46' => 'bg-cyan',
|
165
|
+
'47' => 'bg-white'
|
166
|
+
}
|
167
|
+
|
168
|
+
def self.from_ansi(text)
|
169
|
+
ansi = StringScanner.new(text)
|
170
|
+
html = StringIO.new
|
171
|
+
until ansi.eos?
|
172
|
+
if ansi.scan(/\e\[0?m/)
|
173
|
+
html.print(%(</span>))
|
174
|
+
elsif ansi.scan(/\e\[0?(\d+)m/)
|
175
|
+
# use class instead of style?
|
176
|
+
style = ANSICODES[ansi[1]] || 'text-reset'
|
177
|
+
html.print(%(<span class="#{style}">))
|
178
|
+
else
|
179
|
+
html.print(ansi.scan(/./m))
|
180
|
+
end
|
181
|
+
end
|
182
|
+
html.string
|
183
|
+
end
|
184
|
+
|
185
|
+
# From Rack
|
186
|
+
|
187
|
+
ESCAPE_HTML = {
|
188
|
+
'&' => '&',
|
189
|
+
'<' => '<',
|
190
|
+
'>' => '>',
|
191
|
+
"'" => ''',
|
192
|
+
'"' => '"',
|
193
|
+
'/' => '/'
|
194
|
+
}
|
195
|
+
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
|
196
|
+
|
197
|
+
# Escape ampersands, brackets and quotes to their HTML/XML entities.
|
198
|
+
def self.escape_html(string)
|
199
|
+
string.to_s.gsub(ESCAPE_HTML_PATTERN) { |c| ESCAPE_HTML[c] }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'English'
|
2
|
+
|
3
|
+
module Omnitest
|
4
|
+
# All Omnitest errors and exceptions.
|
5
|
+
module Error
|
6
|
+
# Creates an array of strings, representing a formatted exception,
|
7
|
+
# containing backtrace and nested exception info as necessary, that can
|
8
|
+
# be viewed by a human.
|
9
|
+
#
|
10
|
+
# For example:
|
11
|
+
#
|
12
|
+
# ------Exception-------
|
13
|
+
# Class: Omnitest::StandardError
|
14
|
+
# Message: Failure starting the party
|
15
|
+
# ---Nested Exception---
|
16
|
+
# Class: IOError
|
17
|
+
# Message: not enough directories for a party
|
18
|
+
# ------Backtrace-------
|
19
|
+
# nil
|
20
|
+
# ----------------------
|
21
|
+
#
|
22
|
+
# @param exception [::StandardError] an exception
|
23
|
+
# @return [Array<String>] a formatted message
|
24
|
+
def self.formatted_trace(exception)
|
25
|
+
arr = formatted_exception(exception).dup
|
26
|
+
last = arr.pop
|
27
|
+
if exception.respond_to?(:original) && exception.original
|
28
|
+
arr += formatted_exception(exception.original, 'Nested Exception')
|
29
|
+
last = arr.pop
|
30
|
+
end
|
31
|
+
arr += ['Backtrace'.center(22, '-'), exception.backtrace, last].flatten
|
32
|
+
arr
|
33
|
+
end
|
34
|
+
|
35
|
+
# Creates an array of strings, representing a formatted exception that
|
36
|
+
# can be viewed by a human. Thanks to MiniTest for the inspiration
|
37
|
+
# upon which this output has been designed.
|
38
|
+
#
|
39
|
+
# For example:
|
40
|
+
#
|
41
|
+
# ------Exception-------
|
42
|
+
# Class: Omnitest::StandardError
|
43
|
+
# Message: I have failed you
|
44
|
+
# ----------------------
|
45
|
+
#
|
46
|
+
# @param exception [::StandardError] an exception
|
47
|
+
# @param title [String] a custom title for the message
|
48
|
+
# (default: `"Exception"`)
|
49
|
+
# @return [Array<String>] a formatted message
|
50
|
+
def self.formatted_exception(exception, title = 'Exception')
|
51
|
+
[
|
52
|
+
title.center(22, '-'),
|
53
|
+
"Class: #{exception.class}",
|
54
|
+
"Message: #{exception.message}",
|
55
|
+
''.center(22, '-')
|
56
|
+
]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module ErrorSource
|
61
|
+
def error_source
|
62
|
+
if backtrace_locations
|
63
|
+
source_from_backtrace(backtrace_locations)
|
64
|
+
elsif original && original.backtrace_locations
|
65
|
+
source_from_backtrace(original.backtrace_locations)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def source_from_backtrace(backtrace_locations)
|
70
|
+
error_location = backtrace_locations.delete_if { |l| l.absolute_path =~ /gems\/rspec-/ }.first
|
71
|
+
error_source = File.read(error_location.absolute_path)
|
72
|
+
error_lineno = error_location.lineno - 1 # lineno counts from 1
|
73
|
+
get_dedented_block(error_source, error_lineno)
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_dedented_block(source_text, target_lineno)
|
77
|
+
block = []
|
78
|
+
lines = source_text.lines
|
79
|
+
target_indent = lines[target_lineno][/\A */].size
|
80
|
+
lines[0...target_lineno].reverse.each do |line|
|
81
|
+
indent = line[/\A */].size
|
82
|
+
block.prepend line
|
83
|
+
break if indent < target_indent
|
84
|
+
end
|
85
|
+
lines[target_lineno..lines.size].each do |line|
|
86
|
+
indent = line[/\A */].size
|
87
|
+
block.push line
|
88
|
+
break if indent < target_indent
|
89
|
+
end
|
90
|
+
block.join
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Base exception class from which all Omnitest exceptions derive. This class
|
95
|
+
# nests an exception when this class is re-raised from a rescue block.
|
96
|
+
class StandardError < ::StandardError
|
97
|
+
include Error
|
98
|
+
|
99
|
+
# @return [::StandardError] the original (wrapped) exception
|
100
|
+
attr_reader :original
|
101
|
+
|
102
|
+
# Creates a new StandardError exception which optionally wraps an original
|
103
|
+
# exception if given or detected by checking the `$!` global variable.
|
104
|
+
#
|
105
|
+
# @param msg [String] exception message
|
106
|
+
# @param original [::StandardError] an original exception which will be
|
107
|
+
# wrapped (default: `$ERROR_INFO`)
|
108
|
+
def initialize(msg, original = $ERROR_INFO)
|
109
|
+
super(msg)
|
110
|
+
@original = original
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Base exception class for all exceptions that are caused by user input
|
115
|
+
# errors.
|
116
|
+
class UserError < StandardError; end
|
117
|
+
|
118
|
+
# Base exception class for all exceptions that are caused by incorrect use
|
119
|
+
# of an API.
|
120
|
+
class ClientError < StandardError; end
|
121
|
+
|
122
|
+
# Base exception class for exceptions that are caused by external library
|
123
|
+
# failures which may be temporary.
|
124
|
+
class TransientFailure < StandardError; end
|
125
|
+
|
126
|
+
# Exception class for any exceptions raised when performing an scenario
|
127
|
+
# action.
|
128
|
+
class ActionFailed < TransientFailure; end
|
129
|
+
|
130
|
+
class ExecutionError < TransientFailure
|
131
|
+
attr_accessor :execution_result
|
132
|
+
end
|
133
|
+
|
134
|
+
class << self
|
135
|
+
# Yields to a code block in order to consistently emit a useful crash/error
|
136
|
+
# message and exit appropriately. There are two primary failure conditions:
|
137
|
+
# an expected scenario failure, and any other unexpected failures.
|
138
|
+
#
|
139
|
+
# **Note** This method may call `Kernel.exit` so may not return if the
|
140
|
+
# yielded code block raises an exception.
|
141
|
+
#
|
142
|
+
# ## Scenario Failure
|
143
|
+
#
|
144
|
+
# This is an expected failure scenario which could happen if an scenario
|
145
|
+
# couldn't be created, a Chef run didn't successfully converge, a
|
146
|
+
# post-convergence test suite failed, etc. In other words, you can count on
|
147
|
+
# encountering these failures all the time--this is Omnitest's worldview:
|
148
|
+
# crash early and often. In this case a cleanly formatted exception is
|
149
|
+
# written to `STDERR` and the exception message is written to
|
150
|
+
# the common Omnitest file logger.
|
151
|
+
#
|
152
|
+
# ## Unexpected Failure
|
153
|
+
#
|
154
|
+
# All other forms of `Omnitest::Error` exceptions are considered unexpected
|
155
|
+
# or unplanned exceptions, typically from user configuration errors, driver
|
156
|
+
# or provisioner coding issues or bugs, or internal code issues. Given
|
157
|
+
# a stable release of Omnitest and a solid set of drivers and provisioners,
|
158
|
+
# the most likely cause of this is user configuration error originating in
|
159
|
+
# the `.omnitest.yaml` setup. For this reason, the exception is written to
|
160
|
+
# `STDERR`, a full formatted exception trace is written to the common
|
161
|
+
# Omnitest file logger, and a message is displayed on `STDERR` to the user
|
162
|
+
# informing them to check the log files and check their configuration with
|
163
|
+
# the `omnitest diagnose` subcommand.
|
164
|
+
#
|
165
|
+
# @raise [SystemExit] if an exception is raised in the yielded block
|
166
|
+
def with_friendly_errors
|
167
|
+
yield
|
168
|
+
rescue Omnitest::Skeptic::ScenarioFailure => e
|
169
|
+
Omnitest.mutex.synchronize do
|
170
|
+
handle_scenario_failure(e)
|
171
|
+
end
|
172
|
+
exit 10
|
173
|
+
rescue Omnitest::Error => e
|
174
|
+
Omnitest.mutex.synchronize do
|
175
|
+
handle_error(e)
|
176
|
+
end
|
177
|
+
exit 20
|
178
|
+
end
|
179
|
+
|
180
|
+
# Handles an scenario failure exception.
|
181
|
+
#
|
182
|
+
# @param e [StandardError] an exception to handle
|
183
|
+
# @see Omnitest.with_friendly_errors
|
184
|
+
# @api private
|
185
|
+
def handle_scenario_failure(e)
|
186
|
+
stderr_log(e.message.split(/\s{2,}/))
|
187
|
+
stderr_log(Error.formatted_exception(e.original))
|
188
|
+
file_log(:error, e.message.split(/\s{2,}/).first)
|
189
|
+
debug_log(Error.formatted_trace(e))
|
190
|
+
end
|
191
|
+
|
192
|
+
alias_method :handle_validation_failure, :handle_scenario_failure
|
193
|
+
|
194
|
+
# Handles an unexpected failure exception.
|
195
|
+
#
|
196
|
+
# @param e [StandardError] an exception to handle
|
197
|
+
# @see Omnitest.with_friendly_errors
|
198
|
+
# @api private
|
199
|
+
def handle_error(e)
|
200
|
+
stderr_log(Error.formatted_exception(e))
|
201
|
+
stderr_log('Please see .omnitest/logs/omnitest.log for more details')
|
202
|
+
# stderr_log("Also try running `omnitest diagnose --all` for configuration\n")
|
203
|
+
file_log(:error, Error.formatted_trace(e))
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
|
208
|
+
# Writes an array of lines to the common Omnitest logger's file device at the
|
209
|
+
# given severity level. If the Omnitest logger is set to debug severity, then
|
210
|
+
# the array of lines will also be written to the console output.
|
211
|
+
#
|
212
|
+
# @param level [Symbol,String] the desired log level
|
213
|
+
# @param lines [Array<String>] an array of strings to log
|
214
|
+
# @api private
|
215
|
+
def file_log(level, lines)
|
216
|
+
Array(lines).each do |line|
|
217
|
+
if Omnitest.logger.debug?
|
218
|
+
Omnitest.logger.debug(line)
|
219
|
+
else
|
220
|
+
Omnitest.logger.logdev && Omnitest.logger.logdev.public_send(level, line)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Writes an array of lines to the `STDERR` device.
|
226
|
+
#
|
227
|
+
# @param lines [Array<String>] an array of strings to log
|
228
|
+
# @api private
|
229
|
+
def stderr_log(lines)
|
230
|
+
Array(lines).each do |line|
|
231
|
+
$stderr.puts(Core::Color.colorize(">>>>>> #{line}", :red))
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Writes an array of lines to the common Omnitest debugger with debug
|
236
|
+
# severity.
|
237
|
+
#
|
238
|
+
# @param lines [Array<String>] an array of strings to log
|
239
|
+
# @api private
|
240
|
+
def debug_log(lines)
|
241
|
+
Array(lines).each { |line| Omnitest.logger.debug(line) }
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'omnitest/core/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'omnitest-core'
|
8
|
+
spec.version = Omnitest::Core::VERSION
|
9
|
+
spec.authors = ['Max Lincoln']
|
10
|
+
spec.email = ['max@devopsy.com']
|
11
|
+
spec.summary = 'Shared code for omnitest projects.'
|
12
|
+
spec.homepage = ''
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_dependency 'thor', '~> 0.19'
|
21
|
+
spec.add_dependency 'cause', '~> 0.1'
|
22
|
+
spec.add_dependency 'rouge', '~> 1.7'
|
23
|
+
spec.add_dependency 'hashie', '~> 3.0'
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
spec.add_development_dependency 'rake-notes'
|
27
|
+
spec.add_development_dependency 'simplecov'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 0.18', '<= 0.27'
|
30
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 1.2'
|
31
|
+
spec.add_development_dependency 'aruba'
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: omnitest-core
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Max Lincoln
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.19'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.19'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cause
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rouge
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.7'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: hashie
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.5'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.5'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake-notes
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubocop
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.18'
|
146
|
+
- - "<="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0.27'
|
149
|
+
type: :development
|
150
|
+
prerelease: false
|
151
|
+
version_requirements: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - "~>"
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0.18'
|
156
|
+
- - "<="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0.27'
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: rubocop-rspec
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - "~>"
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '1.2'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - "~>"
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '1.2'
|
173
|
+
- !ruby/object:Gem::Dependency
|
174
|
+
name: aruba
|
175
|
+
requirement: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
type: :development
|
181
|
+
prerelease: false
|
182
|
+
version_requirements: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
description:
|
188
|
+
email:
|
189
|
+
- max@devopsy.com
|
190
|
+
executables: []
|
191
|
+
extensions: []
|
192
|
+
extra_rdoc_files: []
|
193
|
+
files:
|
194
|
+
- ".gitignore"
|
195
|
+
- ".rubocop.yml"
|
196
|
+
- ".rubocop_todo.yml"
|
197
|
+
- Gemfile
|
198
|
+
- LICENSE.txt
|
199
|
+
- README.md
|
200
|
+
- Rakefile
|
201
|
+
- lib/omnitest/core.rb
|
202
|
+
- lib/omnitest/core/cli.rb
|
203
|
+
- lib/omnitest/core/color.rb
|
204
|
+
- lib/omnitest/core/configurable.rb
|
205
|
+
- lib/omnitest/core/file_system.rb
|
206
|
+
- lib/omnitest/core/hashie.rb
|
207
|
+
- lib/omnitest/core/logger.rb
|
208
|
+
- lib/omnitest/core/logging.rb
|
209
|
+
- lib/omnitest/core/util.rb
|
210
|
+
- lib/omnitest/core/version.rb
|
211
|
+
- lib/omnitest/errors.rb
|
212
|
+
- omnitest-core.gemspec
|
213
|
+
homepage: ''
|
214
|
+
licenses:
|
215
|
+
- MIT
|
216
|
+
metadata: {}
|
217
|
+
post_install_message:
|
218
|
+
rdoc_options: []
|
219
|
+
require_paths:
|
220
|
+
- lib
|
221
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
222
|
+
requirements:
|
223
|
+
- - ">="
|
224
|
+
- !ruby/object:Gem::Version
|
225
|
+
version: '0'
|
226
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
227
|
+
requirements:
|
228
|
+
- - ">="
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: '0'
|
231
|
+
requirements: []
|
232
|
+
rubyforge_project:
|
233
|
+
rubygems_version: 2.4.2
|
234
|
+
signing_key:
|
235
|
+
specification_version: 4
|
236
|
+
summary: Shared code for omnitest projects.
|
237
|
+
test_files: []
|