omnitest-psychic 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +5 -0
  5. data/.rubocop_todo.yml +36 -0
  6. data/.travis.yml +10 -0
  7. data/CONTRIBUTING.md +61 -0
  8. data/Gemfile +23 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +234 -0
  11. data/Rakefile +12 -0
  12. data/appveyor.yml +9 -0
  13. data/bin/psychic +4 -0
  14. data/docs/code_samples.md +92 -0
  15. data/docs/index.md +128 -0
  16. data/lib/omnitest/output_helper.rb +84 -0
  17. data/lib/omnitest/psychic.rb +191 -0
  18. data/lib/omnitest/psychic/cli.rb +171 -0
  19. data/lib/omnitest/psychic/code2doc.rb +75 -0
  20. data/lib/omnitest/psychic/code2doc/code_helper.rb +122 -0
  21. data/lib/omnitest/psychic/code2doc/code_segmenter.rb +170 -0
  22. data/lib/omnitest/psychic/code2doc/comment_styles.rb +92 -0
  23. data/lib/omnitest/psychic/command_template.rb +35 -0
  24. data/lib/omnitest/psychic/error.rb +15 -0
  25. data/lib/omnitest/psychic/execution/default_strategy.rb +82 -0
  26. data/lib/omnitest/psychic/execution/env_strategy.rb +12 -0
  27. data/lib/omnitest/psychic/execution/flag_strategy.rb +14 -0
  28. data/lib/omnitest/psychic/execution/token_strategy.rb +68 -0
  29. data/lib/omnitest/psychic/factories/go_factories.rb +14 -0
  30. data/lib/omnitest/psychic/factories/hot_read_task_factory.rb +54 -0
  31. data/lib/omnitest/psychic/factories/java_factories.rb +81 -0
  32. data/lib/omnitest/psychic/factories/powershell_factories.rb +53 -0
  33. data/lib/omnitest/psychic/factories/ruby_factories.rb +56 -0
  34. data/lib/omnitest/psychic/factories/shell_factories.rb +89 -0
  35. data/lib/omnitest/psychic/factories/travis_factories.rb +33 -0
  36. data/lib/omnitest/psychic/factory_manager.rb +46 -0
  37. data/lib/omnitest/psychic/file_finder.rb +69 -0
  38. data/lib/omnitest/psychic/hints.rb +21 -0
  39. data/lib/omnitest/psychic/magic_task_factory.rb +98 -0
  40. data/lib/omnitest/psychic/script.rb +146 -0
  41. data/lib/omnitest/psychic/script_factory.rb +66 -0
  42. data/lib/omnitest/psychic/script_factory_manager.rb +24 -0
  43. data/lib/omnitest/psychic/script_runner.rb +45 -0
  44. data/lib/omnitest/psychic/task.rb +6 -0
  45. data/lib/omnitest/psychic/task_factory_manager.rb +19 -0
  46. data/lib/omnitest/psychic/task_runner.rb +30 -0
  47. data/lib/omnitest/psychic/tokens.rb +51 -0
  48. data/lib/omnitest/psychic/version.rb +5 -0
  49. data/lib/omnitest/psychic/workflow.rb +27 -0
  50. data/lib/omnitest/shell.rb +27 -0
  51. data/lib/omnitest/shell/buff_shellout_executor.rb +41 -0
  52. data/lib/omnitest/shell/execution_result.rb +61 -0
  53. data/lib/omnitest/shell/mixlib_shellout_executor.rb +66 -0
  54. data/mkdocs.yml +6 -0
  55. data/omnitest-psychic.gemspec +36 -0
  56. data/spec/fabricators/shell_fabricator.rb +9 -0
  57. data/spec/omnitest/psychic/code2doc/code_helper_spec.rb +123 -0
  58. data/spec/omnitest/psychic/execution/default_strategy_spec.rb +17 -0
  59. data/spec/omnitest/psychic/execution/env_strategy_spec.rb +17 -0
  60. data/spec/omnitest/psychic/execution/flag_strategy_spec.rb +26 -0
  61. data/spec/omnitest/psychic/execution/token_strategy_spec.rb +26 -0
  62. data/spec/omnitest/psychic/factories/java_factories_spec.rb +35 -0
  63. data/spec/omnitest/psychic/factories/powershell_factories_spec.rb +59 -0
  64. data/spec/omnitest/psychic/factories/ruby_factories_spec.rb +91 -0
  65. data/spec/omnitest/psychic/factories/shell_factories_spec.rb +79 -0
  66. data/spec/omnitest/psychic/factories/travis_factories_spec.rb +78 -0
  67. data/spec/omnitest/psychic/hot_read_task_factory_spec.rb +51 -0
  68. data/spec/omnitest/psychic/script_factory_manager_spec.rb +57 -0
  69. data/spec/omnitest/psychic/script_spec.rb +55 -0
  70. data/spec/omnitest/psychic/shell_spec.rb +68 -0
  71. data/spec/omnitest/psychic/workflow_spec.rb +46 -0
  72. data/spec/omnitest/psychic_spec.rb +170 -0
  73. data/spec/spec_helper.rb +52 -0
  74. 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,12 @@
1
+ module Omnitest
2
+ class Psychic
3
+ module Execution
4
+ class EnvStrategy < DefaultStrategy
5
+ def execute(*extra_args)
6
+ script.env.merge!(script.params)
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Omnitest
2
+ class Psychic
3
+ module Execution
4
+ class FlagStrategy < DefaultStrategy
5
+ def execute(*extra_args)
6
+ script.params.each do |key, value |
7
+ extra_args << "--#{key}=#{value}"
8
+ end
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
14
+ 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,14 @@
1
+ module Omnitest
2
+ class Psychic
3
+ module Factories
4
+ class GoFactory < ScriptFactory
5
+ register_script_factory
6
+ runs '**.go', 7
7
+
8
+ def script(_script)
9
+ 'go run {{source_file}}'
10
+ end
11
+ end
12
+ end
13
+ end
14
+ 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