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,41 @@
1
+ require 'buff/shell_out'
2
+
3
+ module Omnitest
4
+ module Shell
5
+ class BuffShellOutExecutor
6
+ include Omnitest::Core::Logger
7
+ attr_reader :shell
8
+
9
+ def execute(command, opts) # rubocop:disable Metrics/AbcSize
10
+ @logger = opts.delete(:logger) || logger
11
+ cwd = opts[:cwd] || Dir.pwd
12
+ env = opts[:env] || {}
13
+ # @shell.live_stream = IOToLog.new(@logger)
14
+ shell_result = Dir.chdir(cwd) do
15
+ Bundler.with_clean_env do
16
+ Buff::ShellOut.shell_out(command, env)
17
+ end
18
+ end
19
+ fail Errno::ENOENT, shell_result.stderr if shell_result.exitstatus == 127
20
+ execution_result(command, shell_result)
21
+ rescue SystemCallError => e
22
+ execution_error = ExecutionError.new(e)
23
+ execution_error.execution_result = execution_result(command, shell_result)
24
+ raise execution_error
25
+ end
26
+
27
+ private
28
+
29
+ def execution_result(command, shell_result)
30
+ return nil if shell_result.nil?
31
+
32
+ ExecutionResult.new(
33
+ command: command,
34
+ exitstatus: shell_result.exitstatus,
35
+ stdout: shell_result.stdout,
36
+ stderr: shell_result.stderr
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,61 @@
1
+ require 'English'
2
+
3
+ module Omnitest
4
+ module Shell
5
+ class ExecutionError < StandardError
6
+ attr_accessor :execution_result
7
+ end
8
+
9
+ # Stores the result of running a command
10
+ class ExecutionResult
11
+ # @return [String] the command that was executed
12
+ attr_reader :command
13
+ # @return [Integer] the exit code of the process
14
+ attr_reader :exitstatus
15
+ # @return [String] the captured standard output
16
+ attr_reader :stdout
17
+ # @return [String] the captured standard error
18
+ attr_reader :stderr
19
+
20
+ include Omnitest::Core::Util::Hashable
21
+
22
+ # @api private
23
+ def initialize(results)
24
+ @command = results.fetch(:command)
25
+ @exitstatus = results.fetch(:exitstatus)
26
+ # Needs to be UTF-8 to serialize as YAML
27
+ # FIXME: But is serializing to YAML still necessary? Have been using PStore.
28
+ @stdout = results.fetch(:stdout).force_encoding('utf-8')
29
+ @stderr = results.fetch(:stderr).force_encoding('utf-8')
30
+ end
31
+
32
+ # @return [Boolean] true if the command succeeded (exit code 0)
33
+ def successful?
34
+ @exitstatus == 0
35
+ end
36
+
37
+ # Check if the command succeeded and raises and error if it did not.
38
+ # @raises [ExecutionError] if the command did not succeed
39
+ def error!
40
+ unless successful?
41
+ error = ExecutionError.new "#{command} returned exit code #{exitstatus}"
42
+ error.execution_result = self
43
+ fail error
44
+ end
45
+ end
46
+
47
+ # @return [String] a textual summary of the results
48
+ def to_s
49
+ ''"
50
+ Execution Result:
51
+ command: #{command}
52
+ exitstatus: #{exitstatus}
53
+ stdout:
54
+ #{stdout}
55
+ stderr:
56
+ #{stderr}
57
+ "''
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,66 @@
1
+ require 'mixlib/shellout'
2
+
3
+ module Omnitest
4
+ module Shell
5
+ class IOToLog < IO
6
+ def initialize(logger)
7
+ @logger = logger
8
+ @buffer = ''
9
+ end
10
+
11
+ def write(string)
12
+ (@buffer + string).lines.each do |line|
13
+ if line.end_with? "\n"
14
+ @buffer = ''
15
+ @logger.info(line.rstrip)
16
+ else
17
+ @buffer = line
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ class MixlibShellOutExecutor
24
+ include Omnitest::Core::Logger
25
+ attr_reader :shell
26
+
27
+ MIXLIB_SHELLOUT_EXCEPTION_CLASSES = Mixlib::ShellOut.constants.map do|name|
28
+ klass = Mixlib::ShellOut.const_get(name)
29
+ if klass.is_a?(Class) && klass <= RuntimeError
30
+ klass
31
+ else
32
+ nil
33
+ end
34
+ end.compact
35
+
36
+ def execute(command, opts = {}) # rubocop:disable Metrics/AbcSize
37
+ opts[:cwd] = (opts[:cwd] || Dir.pwd).to_s
38
+ @logger = opts.delete(:logger) || logger
39
+ @shell = Mixlib::ShellOut.new(command, opts)
40
+ @shell.live_stream = IOToLog.new(@logger)
41
+ Bundler.with_clean_env do
42
+ @shell.run_command
43
+ end
44
+ execution_result
45
+ rescue SystemCallError, *MIXLIB_SHELLOUT_EXCEPTION_CLASSES, TypeError => e
46
+ # See https://github.com/opscode/mixlib-shellout/issues/62
47
+ execution_error = ExecutionError.new(e)
48
+ execution_error.execution_result = execution_result
49
+ raise execution_error
50
+ end
51
+
52
+ private
53
+
54
+ def execution_result
55
+ return nil if shell.nil?
56
+
57
+ ExecutionResult.new(
58
+ command: shell.command,
59
+ exitstatus: shell.exitstatus,
60
+ stdout: shell.stdout,
61
+ stderr: shell.stderr
62
+ )
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,6 @@
1
+ site_name: Omnitest::Psychic
2
+ pages:
3
+ - getting_started.md
4
+ - running_tasks.md
5
+ - running_scripts.md
6
+
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'omnitest/psychic/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'omnitest-psychic'
8
+ spec.version = Omnitest::Psychic::VERSION
9
+ spec.authors = ['Max Lincoln']
10
+ spec.email = ['max@devopsy.com']
11
+ spec.summary = 'Psychic runs anything.'
12
+ spec.description = 'Provides cross-project aliases for running tasks or similar code samples.'
13
+ # spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'omnitest-core', '~> 0'
22
+ spec.add_dependency 'mixlib-shellout', '~> 1.3' # Used for MRI
23
+ spec.add_dependency 'buff-shell_out', '~> 0.2' # Used for JRuby
24
+ spec.add_dependency 'mustache', '~> 0.99'
25
+ spec.add_dependency 'rouge', '~> 1.0'
26
+ spec.add_development_dependency 'bundler', '~> 1.5'
27
+ spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'rake-notes'
29
+ spec.add_development_dependency 'simplecov'
30
+ spec.add_development_dependency 'rspec', '~> 3.0'
31
+ spec.add_development_dependency 'rubocop', '~> 0.18', '<= 0.27'
32
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.2'
33
+ spec.add_development_dependency 'aruba'
34
+ spec.add_development_dependency 'fabrication'
35
+ spec.add_development_dependency 'inch'
36
+ end
@@ -0,0 +1,9 @@
1
+ Fabricator(:execution_result, from: Omnitest::Shell::ExecutionResult) do
2
+ initialize_with do
3
+ @_klass.new @_transient_attributes
4
+ end # Hash based initialization
5
+ transient command: 'sample command'
6
+ transient exitstatus: 0
7
+ transient stdout: ''
8
+ transient stderr: ''
9
+ end
@@ -0,0 +1,123 @@
1
+ module Omnitest
2
+ class Psychic
3
+ module Code2Doc
4
+ RSpec.describe CodeHelper do
5
+ let(:psychic) { Psychic.new(cwd: current_dir) }
6
+ let(:script) do
7
+ Script.new(psychic, 'test', @source_file)
8
+ end
9
+ let(:source) do
10
+ <<-'eos'
11
+ # This snippet should not be in the output.
12
+ puts "Random: #{rand}"
13
+
14
+ # Snippet: Hello, world!
15
+ puts 'Hello, world!'
16
+
17
+ # Nor should this snippet
18
+ puts 'Done'
19
+ eos
20
+ end
21
+ let(:expected_snippet) do
22
+ <<-'eos'
23
+ puts 'Hello, world!'
24
+ eos
25
+ end
26
+
27
+ around do | example |
28
+ with_files(source: source) do | files |
29
+ @source_file = files.first
30
+ example.run
31
+ end
32
+ end
33
+
34
+ describe '#snippet_after' do
35
+ it 'returns the code block after the match (string)' do
36
+ snippet = script.snippet_after 'Snippet: Hello, world!'
37
+ expect(snippet.strip).to eq(expected_snippet.strip)
38
+ end
39
+
40
+ it 'returns the code block after the match (regex)' do
41
+ snippet = script.snippet_after(/Snippet: .*/)
42
+ expect(snippet.strip).to eq(expected_snippet.strip)
43
+ end
44
+
45
+ it 'returns nothing if no match is found' do
46
+ snippet = script.snippet_after 'Nothing matches'
47
+ expect(snippet).to be_empty
48
+ end
49
+ end
50
+
51
+ describe '#snippet_between' do
52
+ # Yes, whitespace doesn't work very well w/ snippet_between
53
+ let(:expected_snippet) do
54
+ <<-'eos'
55
+ puts "Random: #{rand}"
56
+ # Snippet: Hello, world!
57
+ puts 'Hello, world!'
58
+ eos
59
+ end
60
+
61
+ it 'inserts all code blocks between the matching regexes' do
62
+ snippet = script.snippet_between 'This snippet should not be in the output', 'Nor should this snippet'
63
+ expect(snippet.strip).to eq(expected_snippet.strip)
64
+ end
65
+
66
+ it 'inserts nothing unless both matches are found' do
67
+ # Neither match
68
+ snippet = script.snippet_between 'foo', 'bar'
69
+ expect(snippet.strip).to be_empty
70
+
71
+ # First matches
72
+ snippet = script.snippet_between 'This snippet should not be in the output', 'foo'
73
+ expect(snippet.strip).to be_empty
74
+
75
+ # Last matches
76
+ snippet = script.snippet_between 'foo', 'Nor should this snippet'
77
+ expect(snippet.strip).to be_empty
78
+ end
79
+
80
+ end
81
+
82
+ describe '#code_block' do
83
+ it 'generates markdown code blocks by default' do
84
+ expected = "```ruby\n" + source + "```\n"
85
+ code_block = script.code_block(script.source, 'ruby')
86
+ expect(code_block).to eq(expected)
87
+ end
88
+
89
+ it 'generates rst for format :rst' do
90
+ indented_source = source.lines.map do|line|
91
+ " #{line}"
92
+ end.join("\n")
93
+ expected = ".. code-block:: ruby\n" + indented_source
94
+ code_block = script.code_block(script.source, 'ruby', format: :rst)
95
+ expect(code_block).to eq(expected)
96
+ end
97
+ end
98
+
99
+ def generate_doc_for(template_file, source_file)
100
+ doc_gen = DocumentationGenerator.new(template_file, 'testing')
101
+ script = Script.new(psychic, 'test', source_file)
102
+ script.source_file = source_file
103
+ doc_gen.process(script)
104
+ end
105
+
106
+ def with_files(files)
107
+ tmpfiles = []
108
+ begin
109
+ files.each do |k, v|
110
+ file = Tempfile.new(k.to_s)
111
+ file.write(v)
112
+ file.close
113
+ tmpfiles << file
114
+ end
115
+ yield tmpfiles.map(&:path)
116
+ ensure
117
+ tmpfiles.each(&:unlink)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.shared_examples 'replaces tokens' do
4
+ let(:script) { Omnitest::Psychic.new(cwd: current_dir).script('sample.rb') }
5
+ let(:subject) { described_class.new(script) }
6
+
7
+ describe '#execute' do
8
+ it 'replaces the token with an empty value the param is not set' do
9
+ expect(subject.execute.stdout.strip).to be_empty
10
+ end
11
+
12
+ it 'replaces the token with the value of the param' do
13
+ script.params['token'] = 'foo'
14
+ expect(subject.execute.stdout).to include 'foo'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ module Omnitest
4
+ class Psychic
5
+ module Execution
6
+ RSpec.describe EnvStrategy do
7
+ before(:each) do
8
+ write_file 'sample.rb', <<-eos
9
+ puts ENV['token']
10
+ eos
11
+ end
12
+
13
+ include_examples 'replaces tokens'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module Omnitest
4
+ class Psychic
5
+ module Execution
6
+ RSpec.describe FlagStrategy do
7
+ before(:each) do
8
+ write_file 'sample.rb', <<-eos
9
+ require 'optparse'
10
+
11
+ options = {}
12
+ OptionParser.new do |opts|
13
+ opts.on("--token=TOKEN", "The token") do |v|
14
+ options[:token] = v
15
+ end
16
+ end.parse!
17
+
18
+ puts options[:token]
19
+ eos
20
+ end
21
+
22
+ include_examples 'replaces tokens'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module Omnitest
4
+ class Psychic
5
+ module Execution
6
+ RSpec.describe TokenStrategy do
7
+ before(:each) do
8
+ write_file 'sample.rb', <<-eos
9
+ puts '{token}'
10
+ eos
11
+ end
12
+
13
+ let(:script) { Psychic.new(cwd: current_dir).script('sample') }
14
+ let(:subject) { described_class.new(script) }
15
+
16
+ include_examples 'replaces tokens'
17
+
18
+ describe '#execute' do
19
+ it 'does not permanently alter the file' do
20
+ expect { subject.execute }.to_not change { File.read("#{current_dir}/sample.rb") }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ module Omnitest
2
+ class Psychic
3
+ module Factories
4
+ RSpec.describe JavaFactory do
5
+ let(:psychic) { Psychic.new(cwd: current_dir) }
6
+ let(:shell) { Omnitest::Shell.shell = double('shell') }
7
+ subject { described_class.new(psychic, cwd: current_dir) }
8
+
9
+ before(:each) do
10
+ write_file('src/main/java/HelloWorld.java', '')
11
+ write_file('src/main/java/org/mycompany/FQ.java', '')
12
+ write_file('src/test/java/org/mycompany/FQTest.java', '')
13
+ end
14
+
15
+ describe '#script' do
16
+ let(:hello_world) { psychic.script('hello world') }
17
+ let(:fq) { psychic.script('fq') }
18
+ let(:fqtest) { psychic.script('fqtest') }
19
+
20
+ it 'converts files without a package to a classname only' do
21
+ expect(subject.script hello_world).to eq('java -classpath build/libs/* HelloWorld')
22
+ end
23
+
24
+ it 'converts files with a package to a fully qualified name' do
25
+ expect(subject.script fq).to eq('java -classpath build/libs/* org.mycompany.FQ')
26
+ end
27
+
28
+ it 'converts files with a package to a fully qualified name' do
29
+ expect(subject.script fqtest).to eq('java -classpath build/libs/* org.mycompany.FQTest')
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end