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,92 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
module Code2Doc
|
4
|
+
# This class was extracted from the [Rocco](http://rtomayko.github.com/rocco/) project
|
5
|
+
# which was in turn based on the [Docco](http://jashkenas.github.com/docco/).
|
6
|
+
module CommentStyles
|
7
|
+
class UnknownStyleError < StandardError
|
8
|
+
attr_accessor :extension
|
9
|
+
|
10
|
+
def initialize(extension)
|
11
|
+
@extension = extension
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.infer(extension)
|
16
|
+
extension.tr! '.', ''
|
17
|
+
return extension, COMMENT_STYLES[extension] if COMMENT_STYLES.key? extension
|
18
|
+
|
19
|
+
COMMENT_STYLES.each do | _style_name, style |
|
20
|
+
return extension, style if style[:extensions].include? extension
|
21
|
+
end
|
22
|
+
|
23
|
+
fail UnknownStyleError, extension
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO: This should be YAML, more easily shared with other projects like groc, docco and linguist.
|
27
|
+
|
28
|
+
C_STYLE_COMMENTS = {
|
29
|
+
single: '//',
|
30
|
+
multi: { start: '/**', middle: '*', end: '*/' },
|
31
|
+
heredoc: nil,
|
32
|
+
extensions: %w(c cpp cs java js php scala go)
|
33
|
+
}
|
34
|
+
|
35
|
+
COMMENT_STYLES = {
|
36
|
+
'bash' => { single: '#', multi: nil, extensions: %w(sh) },
|
37
|
+
'c' => C_STYLE_COMMENTS,
|
38
|
+
'coffee-script' => {
|
39
|
+
single: '#',
|
40
|
+
multi: { start: '###', middle: nil, end: '###' },
|
41
|
+
heredoc: nil,
|
42
|
+
extensions: %w(coffee)
|
43
|
+
},
|
44
|
+
'cpp' => C_STYLE_COMMENTS,
|
45
|
+
'csharp' => C_STYLE_COMMENTS,
|
46
|
+
'css' => {
|
47
|
+
single: nil,
|
48
|
+
multi: { start: '/**', middle: '*', end: '*/' },
|
49
|
+
heredoc: nil,
|
50
|
+
extensions: %w(css scss sass)
|
51
|
+
},
|
52
|
+
'html' => {
|
53
|
+
single: nil,
|
54
|
+
multi: { start: '<!--', middle: nil, end: '-->' },
|
55
|
+
heredoc: nil,
|
56
|
+
extensions: %w(html htm)
|
57
|
+
},
|
58
|
+
'java' => C_STYLE_COMMENTS,
|
59
|
+
'js' => C_STYLE_COMMENTS,
|
60
|
+
'lua' => {
|
61
|
+
single: '--',
|
62
|
+
multi: nil,
|
63
|
+
heredoc: nil,
|
64
|
+
extensions: %w(lua)
|
65
|
+
},
|
66
|
+
'php' => C_STYLE_COMMENTS,
|
67
|
+
'python' => {
|
68
|
+
single: '#',
|
69
|
+
multi: { start: '"""', middle: nil, end: '"""' },
|
70
|
+
heredoc: nil,
|
71
|
+
extensions: %w(py)
|
72
|
+
},
|
73
|
+
'rb' => {
|
74
|
+
single: '#',
|
75
|
+
multi: { start: '=begin', middle: nil, end: '=end', idiomatic: false },
|
76
|
+
heredoc: '<<-',
|
77
|
+
extensions: %w(rb)
|
78
|
+
},
|
79
|
+
'scala' => C_STYLE_COMMENTS,
|
80
|
+
'scheme' => { single: ';;', multi: nil, heredoc: nil, extensions: %w(scheme) },
|
81
|
+
'yaml' => { single: '#', multi: nil, heredoc: nil, extensions: %w(yaml) },
|
82
|
+
'xml' => {
|
83
|
+
single: nil,
|
84
|
+
multi: { start: '<!--', middle: nil, end: '-->' },
|
85
|
+
heredoc: nil,
|
86
|
+
extensions: %w(xml xsl xslt)
|
87
|
+
}
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
class CommandTemplate
|
4
|
+
attr_reader :psychic
|
5
|
+
|
6
|
+
def initialize(psychic, template)
|
7
|
+
@psychic = psychic
|
8
|
+
fail ArgumentError, 'Cannot create a nil command' if template.nil?
|
9
|
+
@template = template
|
10
|
+
end
|
11
|
+
|
12
|
+
def command(params = {})
|
13
|
+
Tokens.replace_tokens(build_command, params)
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute(params = {}, *args)
|
17
|
+
shell_opts = args.shift if args.first.is_a? Hash
|
18
|
+
shell_opts ||= {}
|
19
|
+
@psychic.execute(command(params), shell_opts, *args)
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method :to_s, :command
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def build_command
|
27
|
+
@command ||= if @template.respond_to?(:call)
|
28
|
+
@template.call
|
29
|
+
else
|
30
|
+
@template
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
class TaskNotImplementedError < NotImplementedError
|
4
|
+
def initialize(task_alias)
|
5
|
+
super("no active task factories can run a task named #{task_alias}")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class ScriptNotRunnable < NotImplementedError
|
10
|
+
def initialize(script)
|
11
|
+
super("no active script factories no how to run #{script.source_file}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
module Execution
|
4
|
+
class DefaultStrategy < CommandTemplate
|
5
|
+
attr_reader :script
|
6
|
+
|
7
|
+
def initialize(script)
|
8
|
+
@script = script
|
9
|
+
@psychic = script.psychic
|
10
|
+
super(script.psychic, build_command)
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(*args)
|
14
|
+
shell_opts = args.shift if args.first.is_a? Hash
|
15
|
+
shell_opts ||= { env: script.env }
|
16
|
+
expand_parameters
|
17
|
+
params = script.params
|
18
|
+
command_params = { script: script.name, script_file: script.source_file }
|
19
|
+
command_params.merge!(params) unless params.nil?
|
20
|
+
super(command_params, shell_opts, *args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def build_command # rubocop:disable Metrics/AbcSize
|
24
|
+
return @command if defined? @command
|
25
|
+
|
26
|
+
script_factory = psychic.script_factory_manager.factories_for(script).last
|
27
|
+
fail Omnitest::Psychic::ScriptNotRunnable, script if script_factory.nil?
|
28
|
+
|
29
|
+
@command = script_factory.script(script)
|
30
|
+
if script.arguments
|
31
|
+
arguments = script.arguments.map do | arg |
|
32
|
+
Tokens.replace_tokens(arg, script.params)
|
33
|
+
end
|
34
|
+
@arguments = quote(arguments)
|
35
|
+
end
|
36
|
+
@command = "#{@command}" if script.arguments
|
37
|
+
@command = @command.call if @command.respond_to? :call
|
38
|
+
@command = [@command, @arguments].join(' ')
|
39
|
+
end
|
40
|
+
|
41
|
+
def prompt(key)
|
42
|
+
value = script.params[key]
|
43
|
+
if value
|
44
|
+
return value unless psychic.opts[:interactive] == 'always'
|
45
|
+
new_value = cli.ask "Please set a value for #{key} (or enter to confirm #{value.inspect}): "
|
46
|
+
new_value.empty? ? value : new_value
|
47
|
+
else
|
48
|
+
cli.ask "Please set a value for #{key}: "
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def confirm_or_update_parameters(required_parameters)
|
53
|
+
required_parameters.each do | key |
|
54
|
+
script.params[key] = prompt(key)
|
55
|
+
end if interactive?
|
56
|
+
end
|
57
|
+
|
58
|
+
def expand_parameters
|
59
|
+
if script.params.is_a? String
|
60
|
+
script.params = YAML.load(Tokens.replace_tokens(script.params, script.env))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def quote(values)
|
67
|
+
values.map do | value |
|
68
|
+
value.split.size > 1 ? "\"#{value}\"" : value
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def cli
|
73
|
+
psychic.cli
|
74
|
+
end
|
75
|
+
|
76
|
+
def interactive?
|
77
|
+
psychic.interactive?
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
module Execution
|
4
|
+
class TokenStrategy < DefaultStrategy
|
5
|
+
def execute(*extra_args)
|
6
|
+
template = File.read(absolute_file)
|
7
|
+
# Default token pattern/replacement (used by php-opencloud) should be configurable
|
8
|
+
token_handler = Tokens::RegexpTokenHandler.new(template, /["']\{(\w+)\}(["'])/, '\2\1\2')
|
9
|
+
confirm_or_update_parameters(token_handler.tokens)
|
10
|
+
content = token_handler.render(script.params)
|
11
|
+
temporarily_overwrite(absolute_file, content) do
|
12
|
+
super(*extra_args)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def temporarily_overwrite(file, content)
|
19
|
+
backup_file = "#{file}.bak"
|
20
|
+
logger.info("Temporarily replacing tokens in #{file} with actual values")
|
21
|
+
FileUtils.cp(file, backup_file)
|
22
|
+
File.write(file, content)
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
if File.exist? backup_file
|
26
|
+
logger.info("Restoring #{file}")
|
27
|
+
FileUtils.mv(backup_file, absolute_file)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def logger
|
32
|
+
psychic.logger
|
33
|
+
end
|
34
|
+
|
35
|
+
def file
|
36
|
+
script.source_file
|
37
|
+
end
|
38
|
+
|
39
|
+
def absolute_file
|
40
|
+
script.absolute_source_file
|
41
|
+
end
|
42
|
+
|
43
|
+
def backup_file
|
44
|
+
"#{absolute_file}.bak"
|
45
|
+
end
|
46
|
+
|
47
|
+
def should_restore?(file, orig, timing = :before)
|
48
|
+
return true if [timing, 'always']. include? opts[:restore_mode]
|
49
|
+
if interactive?
|
50
|
+
cli.yes? "Would you like to #{file} to #{orig} before running the script?"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def backup_and_overwrite(file)
|
55
|
+
backup_file = "#{file}.bak"
|
56
|
+
if File.exist? backup_file
|
57
|
+
if should_restore?(backup_file, file)
|
58
|
+
FileUtils.mv(backup_file, file)
|
59
|
+
else
|
60
|
+
fail 'Please clear out old backups before rerunning' if File.exist? backup_file
|
61
|
+
end
|
62
|
+
end
|
63
|
+
FileUtils.cp(file, backup_file)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
module Factories
|
4
|
+
class HotReadTaskFactory < MagicTaskFactory
|
5
|
+
register_task_factory
|
6
|
+
|
7
|
+
def initialize(psychic, opts = {})
|
8
|
+
super
|
9
|
+
@tasks = psychic.hints.tasks || {}
|
10
|
+
@known_tasks = @tasks.keys || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def active?
|
14
|
+
!@tasks.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def task(task_alias)
|
18
|
+
return @tasks[task_alias.to_s] if @tasks.include? task_alias.to_s
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class HotReadScriptFactory < ScriptFactory
|
24
|
+
register_script_factory
|
25
|
+
|
26
|
+
def script_hints
|
27
|
+
psychic.hints.scripts
|
28
|
+
end
|
29
|
+
|
30
|
+
def known_script?(script)
|
31
|
+
script_hints.key? script
|
32
|
+
end
|
33
|
+
|
34
|
+
def hot_task_factory
|
35
|
+
psychic.task_factory_manager.active? HotReadTaskFactory
|
36
|
+
end
|
37
|
+
|
38
|
+
def priority_for_script(script)
|
39
|
+
if known_script? script
|
40
|
+
9
|
41
|
+
elsif hot_task_factory.known_task? :run_script
|
42
|
+
7
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def script(script)
|
49
|
+
script_hints[script] || hot_task_factory.task(:run_script)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Omnitest
|
2
|
+
class Psychic
|
3
|
+
module Factories
|
4
|
+
class GradleFactory < MagicTaskFactory
|
5
|
+
TASK_PRIORITY = 6
|
6
|
+
magic_file 'build.gradle'
|
7
|
+
register_task_factory
|
8
|
+
|
9
|
+
task :compile do
|
10
|
+
'gradle assemble'
|
11
|
+
end
|
12
|
+
|
13
|
+
task :test do
|
14
|
+
'gradle test'
|
15
|
+
end
|
16
|
+
|
17
|
+
task :integration do
|
18
|
+
'gradle check'
|
19
|
+
end
|
20
|
+
|
21
|
+
task :bootstrap do
|
22
|
+
# This is for projects using the maven plugin, may need to detect available
|
23
|
+
# tasks w/ gradle install first
|
24
|
+
'gradle assemble'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class MavenFactory < MagicTaskFactory
|
29
|
+
TASK_PRIORITY = 6
|
30
|
+
magic_file 'pom.xml'
|
31
|
+
register_task_factory
|
32
|
+
|
33
|
+
task :compile do
|
34
|
+
'mvn compile'
|
35
|
+
end
|
36
|
+
|
37
|
+
task :test do
|
38
|
+
'mvn test'
|
39
|
+
end
|
40
|
+
|
41
|
+
task :integration do
|
42
|
+
'mvn integration-test'
|
43
|
+
end
|
44
|
+
|
45
|
+
task :bootstrap do
|
46
|
+
# Would compile or something else be more appropriate? install will run tests...
|
47
|
+
'mvn install'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class JavaFactory < ScriptFactory
|
52
|
+
register_script_factory
|
53
|
+
runs '**.java', 7
|
54
|
+
|
55
|
+
def script(script)
|
56
|
+
fully_qualified_name = file_to_fully_qualified_name(script.source_file)
|
57
|
+
"java #{java_opts} #{fully_qualified_name}"
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def file_to_fully_qualified_name(source_file)
|
63
|
+
package = source_file.dirname.to_s
|
64
|
+
package.gsub!('\\', '/')
|
65
|
+
package.gsub!(%r{src/\w+/java}, '')
|
66
|
+
package.gsub!('/', '.')
|
67
|
+
package.gsub!(/\A\./, '')
|
68
|
+
package = nil if package.empty?
|
69
|
+
classname = source_file.basename(source_file.extname)
|
70
|
+
[package, classname].compact.join('.')
|
71
|
+
end
|
72
|
+
|
73
|
+
def java_opts
|
74
|
+
# Need a real way to choose/specify java options
|
75
|
+
# Should run via or get classpath from gradle or maven
|
76
|
+
'-classpath build/libs/*'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|