omnitest-psychic 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +36 -0
- data/.travis.yml +10 -0
- data/CONTRIBUTING.md +61 -0
- data/Gemfile +23 -0
- data/LICENSE.txt +22 -0
- data/README.md +234 -0
- data/Rakefile +12 -0
- data/appveyor.yml +9 -0
- data/bin/psychic +4 -0
- data/docs/code_samples.md +92 -0
- data/docs/index.md +128 -0
- data/lib/omnitest/output_helper.rb +84 -0
- data/lib/omnitest/psychic.rb +191 -0
- data/lib/omnitest/psychic/cli.rb +171 -0
- data/lib/omnitest/psychic/code2doc.rb +75 -0
- data/lib/omnitest/psychic/code2doc/code_helper.rb +122 -0
- data/lib/omnitest/psychic/code2doc/code_segmenter.rb +170 -0
- data/lib/omnitest/psychic/code2doc/comment_styles.rb +92 -0
- data/lib/omnitest/psychic/command_template.rb +35 -0
- data/lib/omnitest/psychic/error.rb +15 -0
- data/lib/omnitest/psychic/execution/default_strategy.rb +82 -0
- data/lib/omnitest/psychic/execution/env_strategy.rb +12 -0
- data/lib/omnitest/psychic/execution/flag_strategy.rb +14 -0
- data/lib/omnitest/psychic/execution/token_strategy.rb +68 -0
- data/lib/omnitest/psychic/factories/go_factories.rb +14 -0
- data/lib/omnitest/psychic/factories/hot_read_task_factory.rb +54 -0
- data/lib/omnitest/psychic/factories/java_factories.rb +81 -0
- data/lib/omnitest/psychic/factories/powershell_factories.rb +53 -0
- data/lib/omnitest/psychic/factories/ruby_factories.rb +56 -0
- data/lib/omnitest/psychic/factories/shell_factories.rb +89 -0
- data/lib/omnitest/psychic/factories/travis_factories.rb +33 -0
- data/lib/omnitest/psychic/factory_manager.rb +46 -0
- data/lib/omnitest/psychic/file_finder.rb +69 -0
- data/lib/omnitest/psychic/hints.rb +21 -0
- data/lib/omnitest/psychic/magic_task_factory.rb +98 -0
- data/lib/omnitest/psychic/script.rb +146 -0
- data/lib/omnitest/psychic/script_factory.rb +66 -0
- data/lib/omnitest/psychic/script_factory_manager.rb +24 -0
- data/lib/omnitest/psychic/script_runner.rb +45 -0
- data/lib/omnitest/psychic/task.rb +6 -0
- data/lib/omnitest/psychic/task_factory_manager.rb +19 -0
- data/lib/omnitest/psychic/task_runner.rb +30 -0
- data/lib/omnitest/psychic/tokens.rb +51 -0
- data/lib/omnitest/psychic/version.rb +5 -0
- data/lib/omnitest/psychic/workflow.rb +27 -0
- data/lib/omnitest/shell.rb +27 -0
- data/lib/omnitest/shell/buff_shellout_executor.rb +41 -0
- data/lib/omnitest/shell/execution_result.rb +61 -0
- data/lib/omnitest/shell/mixlib_shellout_executor.rb +66 -0
- data/mkdocs.yml +6 -0
- data/omnitest-psychic.gemspec +36 -0
- data/spec/fabricators/shell_fabricator.rb +9 -0
- data/spec/omnitest/psychic/code2doc/code_helper_spec.rb +123 -0
- data/spec/omnitest/psychic/execution/default_strategy_spec.rb +17 -0
- data/spec/omnitest/psychic/execution/env_strategy_spec.rb +17 -0
- data/spec/omnitest/psychic/execution/flag_strategy_spec.rb +26 -0
- data/spec/omnitest/psychic/execution/token_strategy_spec.rb +26 -0
- data/spec/omnitest/psychic/factories/java_factories_spec.rb +35 -0
- data/spec/omnitest/psychic/factories/powershell_factories_spec.rb +59 -0
- data/spec/omnitest/psychic/factories/ruby_factories_spec.rb +91 -0
- data/spec/omnitest/psychic/factories/shell_factories_spec.rb +79 -0
- data/spec/omnitest/psychic/factories/travis_factories_spec.rb +78 -0
- data/spec/omnitest/psychic/hot_read_task_factory_spec.rb +51 -0
- data/spec/omnitest/psychic/script_factory_manager_spec.rb +57 -0
- data/spec/omnitest/psychic/script_spec.rb +55 -0
- data/spec/omnitest/psychic/shell_spec.rb +68 -0
- data/spec/omnitest/psychic/workflow_spec.rb +46 -0
- data/spec/omnitest/psychic_spec.rb +170 -0
- data/spec/spec_helper.rb +52 -0
- metadata +352 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
module Factories
|
4
|
+
class PowerShellTaskFactory < MagicTaskFactory
|
5
|
+
TASK_PRIORITY = 1
|
6
|
+
EXTENSIONS = ['.ps1']
|
7
|
+
magic_file 'scripts/*.ps1'
|
8
|
+
register_task_factory
|
9
|
+
|
10
|
+
def initialize(psychic, opts = {})
|
11
|
+
super
|
12
|
+
@known_tasks = Dir["#{cwd}/scripts/*"].map do | script |
|
13
|
+
File.basename(script, File.extname(script)) if EXTENSIONS.include?(File.extname(script))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def task(task_alias)
|
18
|
+
task = task_alias.to_s
|
19
|
+
script = Dir["#{cwd}/scripts/#{task}{.ps1}"].first
|
20
|
+
relativize_cmd(script) if script
|
21
|
+
end
|
22
|
+
|
23
|
+
def active?
|
24
|
+
true if psychic.os_family == :windows
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def relativize_cmd(cmd)
|
30
|
+
cmd = Omnitest::Core::FileSystem.relativize(cmd, cwd)
|
31
|
+
"PowerShell -NoProfile -ExecutionPolicy Bypass -File \"#{cmd}\""
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class PowerShellScriptFactory < ScriptFactory
|
36
|
+
runs '**.ps1', 5
|
37
|
+
|
38
|
+
def active?
|
39
|
+
true if psychic.os_family == :windows
|
40
|
+
end
|
41
|
+
|
42
|
+
def script(script)
|
43
|
+
script = psychic.task('run_script')
|
44
|
+
if script
|
45
|
+
"#{script} #{script.source_file}"
|
46
|
+
else
|
47
|
+
relativize_cmd(script.absolute_source_file)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
module Factories
|
4
|
+
module UsesBundler
|
5
|
+
def bundle_command
|
6
|
+
bundler_active? ? 'bundle exec ' : ''
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def bundler_active?
|
12
|
+
psychic.task_factory_manager.active? BundlerTaskFactory
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class BundlerTaskFactory < MagicTaskFactory
|
17
|
+
TASK_PRIORITY = 2
|
18
|
+
magic_file 'Gemfile'
|
19
|
+
magic_file '.bundle/config'
|
20
|
+
magic_env_var 'BUNDLE_GEMFILE'
|
21
|
+
register_task_factory
|
22
|
+
|
23
|
+
def active?
|
24
|
+
# Avoid detecting omnitest's own BUNDLE_GEMFILE variable
|
25
|
+
Bundler.with_clean_env do
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
task :bootstrap do
|
31
|
+
'bundle install'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class RakeFactory < MagicTaskFactory
|
36
|
+
include UsesBundler
|
37
|
+
magic_file 'Rakefile'
|
38
|
+
register_task_factory
|
39
|
+
|
40
|
+
task :test do
|
41
|
+
[bundle_command, 'rake'].join
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class RubyFactory < ScriptFactory
|
46
|
+
include UsesBundler
|
47
|
+
register_script_factory
|
48
|
+
runs '**.rb', 8
|
49
|
+
|
50
|
+
def script(script)
|
51
|
+
[bundle_command, "ruby #{script.source_file}"].join
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
module Factories
|
4
|
+
module ShellBase
|
5
|
+
EXTENSIONS = ['.sh', '']
|
6
|
+
|
7
|
+
def active?
|
8
|
+
true unless psychic.os_family == :windows
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def relativize_cmd(cmd)
|
14
|
+
cmd = Omnitest::Core::FileSystem.relativize(cmd, cwd)
|
15
|
+
"./#{cmd}" unless cmd.to_s.start_with? '/'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ShellTaskFactory < MagicTaskFactory
|
20
|
+
include ShellBase
|
21
|
+
TASK_PRIORITY = 1
|
22
|
+
magic_file '{script,scripts}/*'
|
23
|
+
register_task_factory
|
24
|
+
|
25
|
+
def initialize(*args)
|
26
|
+
super
|
27
|
+
@known_tasks = Dir.glob("#{cwd}/{script,scripts}/*", File::FNM_CASEFOLD).map do | script |
|
28
|
+
File.basename(script, File.extname(script)) if EXTENSIONS.include?(File.extname(script))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def task(task_alias)
|
33
|
+
task = task_alias.to_s
|
34
|
+
script = Dir.glob("#{cwd}/{script,scripts}/#{task}{.sh,}", File::FNM_CASEFOLD).first
|
35
|
+
relativize_cmd(script) if script
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ShellScriptFactory < ScriptFactory
|
40
|
+
include ShellBase
|
41
|
+
register_script_factory
|
42
|
+
runs '**.sh', 5
|
43
|
+
|
44
|
+
def shell_task_factory
|
45
|
+
psychic.task_factory_manager.active? ShellTaskFactory
|
46
|
+
end
|
47
|
+
|
48
|
+
def priority_for_script(script)
|
49
|
+
return 8 if shell_task_factory.known_task? :run_script
|
50
|
+
|
51
|
+
case script.extname
|
52
|
+
when '.sh'
|
53
|
+
9
|
54
|
+
when ''
|
55
|
+
7
|
56
|
+
else
|
57
|
+
5 if shebang?(script)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def script(script)
|
62
|
+
base_command = run_script_command
|
63
|
+
if base_command
|
64
|
+
"#{base_command} #{script.source_file}"
|
65
|
+
else
|
66
|
+
relativize_cmd(script.absolute_source_file)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
def run_script_command
|
73
|
+
psychic.task('run_script')
|
74
|
+
rescue TaskNotImplementedError
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def shebang?(script)
|
79
|
+
first_line = script.source.lines.first
|
80
|
+
first_line && first_line.match(/\#\!/)
|
81
|
+
rescue => e
|
82
|
+
logger.warn("Could not read #{script.source_file}: #{e.message}")
|
83
|
+
# Could be binary, unknown encoding, ...
|
84
|
+
false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
module Factories
|
4
|
+
class TravisTaskFactory < MagicTaskFactory
|
5
|
+
TASK_PRIORITY = 2
|
6
|
+
magic_file '.travis.yml'
|
7
|
+
register_task_factory
|
8
|
+
|
9
|
+
def active?
|
10
|
+
super && travis_allowed? && travis_build_installed?
|
11
|
+
end
|
12
|
+
|
13
|
+
def travis_allowed?
|
14
|
+
psychic.opts[:travis]
|
15
|
+
end
|
16
|
+
|
17
|
+
def travis_build_installed?
|
18
|
+
# check that the travis-build extension is installed
|
19
|
+
# HACK: use the MixlibShellOutExecutor
|
20
|
+
Bundler.with_clean_env { `travis help --skip-version-check`.match(/run/) }
|
21
|
+
end
|
22
|
+
|
23
|
+
task :bootstrap do
|
24
|
+
psychic.execute('travis run --print --skip-version-check install').stdout
|
25
|
+
end
|
26
|
+
|
27
|
+
task :unittest do
|
28
|
+
psychic.execute('travis run --print --skip-version-check script').stdout
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
class FactoryManager
|
4
|
+
include Omnitest::Core::Logger
|
5
|
+
|
6
|
+
BUILT_IN_DIR = File.expand_path('../factories', __FILE__)
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def autoload_factories!
|
10
|
+
# Load built-in task factories
|
11
|
+
Dir.glob("#{BUILT_IN_DIR}/*.rb", File::FNM_CASEFOLD).each do |task_factory_file|
|
12
|
+
require task_factory_file
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def factory_classes
|
17
|
+
@factory_classes ||= Set.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def register_factory(klass)
|
21
|
+
factory_classes.add klass
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear
|
25
|
+
factory_classes.clear
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :factories
|
30
|
+
|
31
|
+
def initialize(*args)
|
32
|
+
@factories = self.class.factory_classes.map { |k| k.new(*args) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def active_factories
|
36
|
+
factories.select(&:active?).sort_by(&:priority)
|
37
|
+
end
|
38
|
+
|
39
|
+
def active?(klass)
|
40
|
+
factories.find do | factory |
|
41
|
+
factory.is_a? klass
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
class FileFinder
|
4
|
+
attr_reader :search_path, :ignored_patterns
|
5
|
+
|
6
|
+
def initialize(search_path, ignored_patterns)
|
7
|
+
@search_path = search_path
|
8
|
+
@ignored_patterns = ignored_patterns || read_gitignore(search_path)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Finds a file by loosely matching the file name to a scenario name
|
12
|
+
def find_file(name)
|
13
|
+
return name if File.exist? File.expand_path(name, search_path)
|
14
|
+
|
15
|
+
# Filter out ignored files
|
16
|
+
files = potential_files(name).select do |f|
|
17
|
+
!ignored?(f)
|
18
|
+
end
|
19
|
+
|
20
|
+
if block_given?
|
21
|
+
file = yield files
|
22
|
+
else
|
23
|
+
# Select the shortest path, likely the best match
|
24
|
+
file = files.min_by(&:length)
|
25
|
+
end
|
26
|
+
|
27
|
+
fail Errno::ENOENT, "No file was found for #{name} within #{search_path}" if file.nil?
|
28
|
+
Omnitest::Core::FileSystem.relativize(file, search_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def potential_files(name)
|
32
|
+
slugified_name = Omnitest::Core::FileSystem.slugify(name)
|
33
|
+
glob_string = "#{search_path}/**/*#{slugified_name}*.*"
|
34
|
+
potential_files = Dir.glob(glob_string, File::FNM_CASEFOLD)
|
35
|
+
potential_files.concat Dir.glob(glob_string.gsub('_', '-'), File::FNM_CASEFOLD)
|
36
|
+
potential_files.concat Dir.glob(glob_string.gsub('_', ''), File::FNM_CASEFOLD)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @api private
|
42
|
+
def read_gitignore(dir)
|
43
|
+
gitignore_file = "#{dir}/.gitignore"
|
44
|
+
File.read(gitignore_file)
|
45
|
+
rescue
|
46
|
+
''
|
47
|
+
end
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
def ignored?(target_file)
|
51
|
+
# Trying to match the git ignore rules but there's some discrepencies.
|
52
|
+
ignored_patterns.split.find do |pattern|
|
53
|
+
# if git ignores a folder, we should ignore all files it contains
|
54
|
+
pattern = "#{pattern}**" if pattern[-1] == '/'
|
55
|
+
started_with_slash = pattern.start_with? '/'
|
56
|
+
|
57
|
+
pattern.gsub!(%r{\A/}, '') # remove leading slashes since we're searching from root
|
58
|
+
file = Omnitest::Core::FileSystem.relativize(target_file, search_path)
|
59
|
+
ignored = file.fnmatch? pattern
|
60
|
+
ignored || (file.fnmatch? "**/#{pattern}" unless started_with_slash)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.find_file_by_alias(file_alias, search_path, ignored_patterns = nil, &block)
|
65
|
+
FileFinder.new(search_path, ignored_patterns).find_file(file_alias, &block)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
class Hints < Omnitest::Core::Dash
|
4
|
+
field :options, Hash, default: {}
|
5
|
+
field :tasks, Hash[String => String]
|
6
|
+
field :scripts, Hash[String => Pathname]
|
7
|
+
|
8
|
+
def options
|
9
|
+
self[:options] ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def tasks
|
13
|
+
self[:tasks] ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def scripts
|
17
|
+
self[:scripts] ||= {}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
class MagicTaskFactory
|
4
|
+
include Omnitest::Core::Logger
|
5
|
+
extend Forwardable
|
6
|
+
def_delegators :psychic, :cwd, :env, :logger
|
7
|
+
|
8
|
+
TASK_PRIORITY = 5
|
9
|
+
|
10
|
+
attr_reader :known_tasks, :tasks, :hints, :priority, :psychic
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def register_task_factory
|
14
|
+
Omnitest::Psychic::TaskFactoryManager.register_factory(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def magic_file_patterns
|
18
|
+
@magic_file_patterns ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
def magic_file(pattern) # rubocop:disable Style/TrivialAccessors
|
22
|
+
magic_file_patterns << pattern
|
23
|
+
end
|
24
|
+
|
25
|
+
def magic_env_vars
|
26
|
+
@magic_env_vars ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def magic_env_var(var)
|
30
|
+
magic_env_vars << var
|
31
|
+
end
|
32
|
+
|
33
|
+
def known_tasks
|
34
|
+
@known_tasks ||= []
|
35
|
+
end
|
36
|
+
|
37
|
+
def tasks
|
38
|
+
@tasks ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def task(name, &block)
|
42
|
+
name = name.to_s
|
43
|
+
tasks[name] = block
|
44
|
+
known_tasks << name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(psychic, opts = {})
|
49
|
+
@psychic = psychic
|
50
|
+
@opts = opts
|
51
|
+
@priority = self.class::TASK_PRIORITY
|
52
|
+
init_attr(:known_tasks) { self.class.known_tasks }
|
53
|
+
init_attr(:tasks) { self.class.tasks }
|
54
|
+
end
|
55
|
+
|
56
|
+
def task(task_alias, *_args)
|
57
|
+
task_alias = task_alias.to_s
|
58
|
+
task = tasks[task_alias] if tasks.include? task_alias
|
59
|
+
task = instance_eval(&task) if task.respond_to? :call
|
60
|
+
fail Omnitest::Psychic::TaskNotImplementedError, task_alias if task.nil?
|
61
|
+
task
|
62
|
+
end
|
63
|
+
|
64
|
+
def active?
|
65
|
+
self.class.magic_file_patterns.each do | pattern |
|
66
|
+
return true unless Dir.glob("#{cwd}/#{pattern}", File::FNM_CASEFOLD).empty?
|
67
|
+
end
|
68
|
+
self.class.magic_env_vars.each do | var |
|
69
|
+
return true if ENV[var]
|
70
|
+
end
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
def known_task?(task_alias)
|
75
|
+
known_tasks.include?(task_alias.to_s)
|
76
|
+
end
|
77
|
+
|
78
|
+
def priority_for_task(task_alias)
|
79
|
+
priority if known_task? task_alias
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def init_attr(var)
|
85
|
+
var_name = "@#{var}"
|
86
|
+
var_value = @opts[var]
|
87
|
+
var_value = yield if var_value.nil? && block_given?
|
88
|
+
instance_variable_set(var_name, var_value)
|
89
|
+
end
|
90
|
+
|
91
|
+
def init_attrs(*vars)
|
92
|
+
vars.each do | var |
|
93
|
+
init_attr var
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|