clintegracon 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +158 -0
  7. data/Rakefile +60 -0
  8. data/clintegracon.gemspec +34 -0
  9. data/lib/CLIntegracon.rb +9 -0
  10. data/lib/CLIntegracon/adapter/bacon.rb +208 -0
  11. data/lib/CLIntegracon/configuration.rb +67 -0
  12. data/lib/CLIntegracon/diff.rb +81 -0
  13. data/lib/CLIntegracon/file_tree_spec.rb +213 -0
  14. data/lib/CLIntegracon/file_tree_spec_context.rb +180 -0
  15. data/lib/CLIntegracon/formatter.rb +77 -0
  16. data/lib/CLIntegracon/subject.rb +128 -0
  17. data/lib/CLIntegracon/version.rb +3 -0
  18. data/spec/bacon/execution_output.txt +72 -0
  19. data/spec/bacon/spec_helper.rb +60 -0
  20. data/spec/fixtures/bin/coffeemaker.rb +58 -0
  21. data/spec/integration/coffeemaker_help/after/execution_output.txt +23 -0
  22. data/spec/integration/coffeemaker_help/before/.gitkeep +0 -0
  23. data/spec/integration/coffeemaker_no_milk/after/BlackEye.brewed-coffee +1 -0
  24. data/spec/integration/coffeemaker_no_milk/after/CaPheSuaDa.brewed-coffee +1 -0
  25. data/spec/integration/coffeemaker_no_milk/after/Coffeemakerfile.yml +5 -0
  26. data/spec/integration/coffeemaker_no_milk/after/RedTux.brewed-coffee +1 -0
  27. data/spec/integration/coffeemaker_no_milk/after/execution_output.txt +6 -0
  28. data/spec/integration/coffeemaker_no_milk/before/Coffeemakerfile.yml +5 -0
  29. data/spec/integration/coffeemaker_sweetner_honey/after/Affogato.brewed-coffee +2 -0
  30. data/spec/integration/coffeemaker_sweetner_honey/after/BlackEye.brewed-coffee +2 -0
  31. data/spec/integration/coffeemaker_sweetner_honey/after/Coffeemakerfile.yml +3 -0
  32. data/spec/integration/coffeemaker_sweetner_honey/after/RedTux.brewed-coffee +2 -0
  33. data/spec/integration/coffeemaker_sweetner_honey/after/execution_output.txt +4 -0
  34. data/spec/integration/coffeemaker_sweetner_honey/before/Coffeemakerfile.yml +3 -0
  35. data/spec/unit/adapter/bacon_spec.rb +187 -0
  36. data/spec/unit/configuration_spec.rb +72 -0
  37. metadata +176 -0
@@ -0,0 +1,77 @@
1
+ require 'colored'
2
+
3
+ module CLIntegracon
4
+ class Formatter
5
+
6
+ # @return [FileTreeSpec]
7
+ # the spec
8
+ attr_reader :spec
9
+
10
+ # Initialize
11
+ #
12
+ # @param [FileTreeSpec] spec
13
+ # the spec
14
+ #
15
+ def initialize(spec)
16
+ super()
17
+ @spec = spec
18
+ end
19
+
20
+ # Return a description text for an expectation that a file path
21
+ # was expected to exist, but is missing.
22
+ #
23
+ # @param [Pathname] file_path
24
+ # the file path which was expected to exist
25
+ #
26
+ # @return [String]
27
+ #
28
+ def describe_missing_file(file_path)
29
+ description = []
30
+ description << "Missing file for #{spec.spec_folder}:"
31
+ description << " * #{file_path.to_s.red}"
32
+ description * "\n"
33
+ end
34
+
35
+ # Return a description text for an expectation that certain file paths
36
+ # were unexpected.
37
+ #
38
+ # @param [Array<Pathname>] file_paths
39
+ #
40
+ # @return [String]
41
+ #
42
+ def describe_unexpected_files(file_paths)
43
+ description = []
44
+ description << "Unexpected files for #{spec.spec_folder}:"
45
+ description += file_paths.map { |f| " * #{f.to_s.green}" }
46
+ description * "\n"
47
+ end
48
+
49
+ # Return a description text for an expectation that two files were
50
+ # expected to be the same, but are not.
51
+ #
52
+ # @param [Diff] diff
53
+ # the diff which holds the difference
54
+ #
55
+ # @param [Integer] max_width
56
+ # the max width of the terminal to print matching separators
57
+ #
58
+ # @return [String]
59
+ #
60
+ def describe_file_diff(diff, max_width=80)
61
+ description = []
62
+ description << "File comparison error `#{diff.relative_path}` for #{spec.spec_folder}:"
63
+ description << "--- DIFF ".ljust(max_width, '-')
64
+ description += diff.map do |line|
65
+ case line
66
+ when /^\+/ then line.green
67
+ when /^-/ then line.red
68
+ else line
69
+ end.gsub("\n",'')
70
+ end
71
+ description << "--- END ".ljust(max_width, '-')
72
+ description << ''
73
+ description * "\n"
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,128 @@
1
+ module CLIntegracon
2
+ class Subject
3
+
4
+ #-----------------------------------------------------------------------------#
5
+
6
+ # @!group Attributes
7
+
8
+ # @return [String]
9
+ # The name of the binary to use for the tests
10
+ attr_accessor :name
11
+
12
+ # @return [String]
13
+ # The executable statement to use for the tests
14
+ attr_accessor :executable
15
+
16
+ # @return [Hash<String,String>]
17
+ # The environment variables which will always been defined for the executable
18
+ # on launch should not been passed explicitly every time to the launch method.
19
+ attr_accessor :environment_vars
20
+
21
+ # @return [Array<String>]
22
+ # The arguments which will always passed to the executable on launch and
23
+ # should not been passed explicitly every time to the launch method.
24
+ # Those are added behind the arguments given on +launch+.
25
+ attr_accessor :default_args
26
+
27
+ # @return [Hash<String,String>]
28
+ # The special paths, which are not relative to the project, when the statement
29
+ # will be executed. These are paths were side-effects occur, like manipulation
30
+ # of user configurations in dot files or caching-specific directories.
31
+ attr_accessor :special_paths
32
+
33
+ # @return [String]
34
+ # The path where the output of the executable will be written to.
35
+ attr_accessor :output_path
36
+
37
+
38
+ #-----------------------------------------------------------------------------#
39
+
40
+ # @!group Initializer
41
+
42
+ # "Designated" initializer
43
+ #
44
+ # @param [String] name
45
+ # The name of the binary
46
+ #
47
+ # @param [String] executable
48
+ # The executable subject statement (optional)
49
+ #
50
+ def initialize(name='subject', executable=nil)
51
+ self.name = name
52
+ self.executable = executable || name
53
+ self.environment_vars = {}
54
+ self.default_args = []
55
+ self.special_paths = {}
56
+ self.output_path = 'execution_output.txt'
57
+ end
58
+
59
+
60
+ #-----------------------------------------------------------------------------#
61
+
62
+ # @!group DSL-like Setter
63
+
64
+ # Define a path in the user directory as special path
65
+ #
66
+ # @param [String] path
67
+ # The path
68
+ #
69
+ # @param [String] name
70
+ # The name of the path, or the basename of the given path
71
+ #
72
+ def has_special_path(path, name=nil)
73
+ name ||= File.basename path
74
+ self.special_paths[name] = path
75
+ end
76
+
77
+ # Define a path in the user directory as special path
78
+ #
79
+ # @param [String] path
80
+ # The path
81
+ #
82
+ # @param [String] name
83
+ # The name of the path, or the basename of the given path
84
+ #
85
+ def has_special_user_path(path, name=nil)
86
+ self.has_special_path %r[/Users/.*/#{path.to_s}], name
87
+ end
88
+
89
+ #-----------------------------------------------------------------------------#
90
+
91
+ # @!group Interaction
92
+
93
+ # Runs the executable with the given arguments in the temporary directory.
94
+ #
95
+ # @note: You can check by `$?.success?` if the execution succeeded.
96
+ #
97
+ # @param [String] head_arguments
98
+ # The arguments to pass to the executable before the default arguments.
99
+ #
100
+ # @param [String] tail_arguments
101
+ # The arguments to pass to the executable after the default arguments.
102
+ #
103
+ # @return [String]
104
+ # The output, which is emitted while execution from the binary.
105
+ #
106
+ def launch(head_arguments='', tail_arguments='')
107
+ vars = environment_vars.map { |key,value| "#{key}=#{value}" }.join ' '
108
+ args = [head_arguments, default_args, tail_arguments].flatten.compact.select { |s| s.length > 0 }.join ' '
109
+ command = "#{vars} #{executable} #{args} 2>&1"
110
+
111
+ output = `#{command}`
112
+
113
+ File.open(output_path, 'w') do |file|
114
+ file.write command.sub(executable, name)
115
+ file.write "\n"
116
+
117
+ special_paths.each do |key, path|
118
+ output.gsub!(path, key)
119
+ end
120
+
121
+ file.write output
122
+ end
123
+
124
+ output
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,3 @@
1
+ module CLIntegracon
2
+ VERSION = "0.4.1"
3
+ end
@@ -0,0 +1,72 @@
1
+ CLIntegracon::Adapter::Bacon
2
+ coffee-maker
3
+ Brew recipes
4
+ without milk
5
+ - $ coffee-maker --no-milk
6
+ - BlackEye.brewed-coffee
7
+ - CaPheSuaDa.brewed-coffee
8
+ - Coffeemakerfile.yml
9
+ - execution_output.txt
10
+ - RedTux.brewed-coffee
11
+ - should not produce unexpected files [FAILED]
12
+ with honey as sweetner
13
+ - $ coffee-maker --sweetner=honey
14
+ - Affogato.brewed-coffee [FAILED]
15
+ - BlackEye.brewed-coffee [FAILED]
16
+ - Coffeemakerfile.yml
17
+ - execution_output.txt
18
+ - RedTux.brewed-coffee [FAILED]
19
+ - should not produce unexpected files
20
+ Get help
21
+ - $ coffee-maker --help
22
+ - execution_output.txt
23
+ - should not produce unexpected files
24
+
25
+ Bacon::Error: Unexpected files for coffeemaker_no_milk:
26
+ * Affogato.brewed-coffee
27
+ ./spec/bacon/spec_helper.rb:45
28
+ ./spec/bacon/spec_helper.rb:44
29
+ ./spec/bacon/spec_helper.rb:42
30
+ ./spec/bacon/spec_helper.rb:17
31
+ ./spec/bacon/spec_helper.rb:15
32
+ $GEM_HOME/bin/ruby_executable_hooks:15
33
+
34
+ Bacon::Error: File comparison error `Affogato.brewed-coffee` for coffeemaker_sweetner_honey:
35
+ --- DIFF -----------------------------------------------------------------------
36
+ class Affogato < BrewedCoffee
37
+ + @milk = true
38
+ @sweetner = honey
39
+ --- END ------------------------------------------------------------------------
40
+
41
+ ./spec/bacon/spec_helper.rb:49
42
+ ./spec/bacon/spec_helper.rb:48
43
+ ./spec/bacon/spec_helper.rb:42
44
+ ./spec/bacon/spec_helper.rb:17
45
+ ./spec/bacon/spec_helper.rb:15
46
+ $GEM_HOME/bin/ruby_executable_hooks:15
47
+
48
+ Bacon::Error: Missing file for coffeemaker_sweetner_honey:
49
+ * BlackEye.brewed-coffee
50
+ ./spec/bacon/spec_helper.rb:49
51
+ ./spec/bacon/spec_helper.rb:48
52
+ ./spec/bacon/spec_helper.rb:42
53
+ ./spec/bacon/spec_helper.rb:17
54
+ ./spec/bacon/spec_helper.rb:15
55
+ $GEM_HOME/bin/ruby_executable_hooks:15
56
+
57
+ Bacon::Error: File comparison error `RedTux.brewed-coffee` for coffeemaker_sweetner_honey:
58
+ --- DIFF -----------------------------------------------------------------------
59
+ class RedTux < BrewedCoffee
60
+ - @sweetner = sugar
61
+ + @milk = true
62
+ + @sweetner = honey
63
+ --- END ------------------------------------------------------------------------
64
+
65
+ ./spec/bacon/spec_helper.rb:49
66
+ ./spec/bacon/spec_helper.rb:48
67
+ ./spec/bacon/spec_helper.rb:42
68
+ ./spec/bacon/spec_helper.rb:17
69
+ ./spec/bacon/spec_helper.rb:15
70
+ $GEM_HOME/bin/ruby_executable_hooks:15
71
+
72
+ 17 specifications (27 requirements), 4 failures, 0 errors
@@ -0,0 +1,60 @@
1
+ require 'bacon'
2
+ require 'CLIntegracon'
3
+
4
+ ROOT = Pathname.new(File.expand_path('../../../', __FILE__))
5
+ BIN = ROOT + 'spec/fixtures/bin'
6
+
7
+ CLIntegracon.configure do |c|
8
+ c.context.spec_path = ROOT + 'spec/integration'
9
+ c.context.temp_path = ROOT + 'tmp/bacon_specs'
10
+
11
+ c.hook_into :bacon
12
+ end
13
+
14
+
15
+ describe CLIntegracon::Adapter::Bacon do
16
+
17
+ describe_cli 'coffee-maker' do
18
+
19
+ subject do |s|
20
+ s.name = 'coffee-maker'
21
+ s.executable = "bundle exec ruby #{BIN}/coffeemaker.rb"
22
+ s.environment_vars = {
23
+ 'COFFEE_MAKER_FILE' => 'Coffeemakerfile.yml'
24
+ }
25
+ s.default_args = [
26
+ '--verbose',
27
+ '--no-ansi'
28
+ ]
29
+ s.has_special_path ROOT.to_s, 'ROOT'
30
+ s.has_special_path `bundle show claide`.rstrip, 'CLAIDE_SRC'
31
+ end
32
+
33
+ context do |c|
34
+ c.ignores '.DS_Store'
35
+ c.ignores '.gitkeep'
36
+
37
+ c.has_special_handling_for 'execution_output.txt' do |path|
38
+ File.read(path).gsub(/:in `<main>'$/, '') # workaround different stack trace format by ruby-1.8.7
39
+ end
40
+ end
41
+
42
+ describe 'Brew recipes' do
43
+
44
+ describe 'without milk' do
45
+ behaves_like cli_spec('coffeemaker_no_milk', '--no-milk')
46
+ end
47
+
48
+ describe 'with honey as sweetner' do
49
+ behaves_like cli_spec('coffeemaker_sweetner_honey', '--sweetner=honey')
50
+ end
51
+
52
+ end
53
+
54
+ describe 'Get help' do
55
+ behaves_like cli_spec('coffeemaker_help', '--help')
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,58 @@
1
+ #encoding: utf-8
2
+
3
+ require 'claide'
4
+ require 'colored'
5
+ require 'yaml'
6
+
7
+ # Don’t worry, this makes no sense, just an example
8
+ # command which modifies the file system.
9
+ class CoffeeMaker < CLAide::Command
10
+ self.description = 'Make delicious coffee from the comfort of your terminal.'
11
+
12
+ def self.options
13
+ [
14
+ ['--no-milk', 'Don’t add milk'],
15
+ ['--sweetner=[sugar|honey]', 'Use one of the available sweetners'],
16
+ ].concat(super)
17
+ end
18
+
19
+ def initialize(argv)
20
+ @add_milk = argv.flag?('milk', true)
21
+ @sweetner = argv.option('sweetner')
22
+ @config_file = ENV['COFFEE_MAKER_FILE'] || 'Coffeemakerfile'
23
+ super
24
+ end
25
+
26
+ def validate!
27
+ super
28
+ if @sweetner && !%w(sugar honey).include?(@sweetner)
29
+ help! "'#{@sweetner}' is not a valid sweetner."
30
+ end
31
+ end
32
+
33
+ def brew(recipe)
34
+ File.open recipe+'.brewed-coffee', 'w' do |f|
35
+ f.write "class #{recipe} < BrewedCoffee\n"
36
+ if @add_milk
37
+ f.write " @milk = true\n"
38
+ end
39
+ if @sweetner
40
+ f.write " @sweetner = #{@sweetner}\n"
41
+ end
42
+ end
43
+ end
44
+
45
+ def run
46
+ @config = YAML.load_file @config_file
47
+ if @config['recipes'] == nil
48
+ help! "Didn’t found any `recipes` in the Coffeemakerfile.yml."
49
+ end
50
+ @config['recipes'].each do |recipe|
51
+ puts "* Brewing #{recipe}"
52
+ brew(recipe)
53
+ end
54
+ puts '* Enjoy!'
55
+ end
56
+ end
57
+
58
+ CoffeeMaker.run(ARGV)
@@ -0,0 +1,23 @@
1
+ COFFEE_MAKER_FILE=Coffeemakerfile.yml coffee-maker --help --verbose --no-ansi 2>&1
2
+ Usage:
3
+
4
+ $ coffee-maker
5
+
6
+ Make delicious coffee from the comfort of your terminal.
7
+
8
+ Options:
9
+
10
+ --no-milk Don’t add milk
11
+ --sweetner=[sugar|honey] Use one of the available sweetners
12
+ --completion-script Print the auto-completion script
13
+ --version Show the version of the tool
14
+ --verbose Show more debugging information
15
+ --no-ansi Show output without ANSI codes
16
+ --help Show help banner of specified command
17
+
18
+ CLAIDE_SRC/lib/claide/command.rb:348:in `help!'
19
+ CLAIDE_SRC/lib/claide/command.rb:466:in `help!'
20
+ CLAIDE_SRC/lib/claide/command.rb:434:in `validate!'
21
+ ROOT/spec/fixtures/bin/coffeemaker.rb:27:in `validate!'
22
+ CLAIDE_SRC/lib/claide/command.rb:280:in `run'
23
+ ROOT/spec/fixtures/bin/coffeemaker.rb:58:in `<main>'
@@ -0,0 +1 @@
1
+ class BlackEye < BrewedCoffee
@@ -0,0 +1 @@
1
+ class CaPheSuaDa < BrewedCoffee
@@ -0,0 +1,5 @@
1
+ recipes:
2
+ - BlackEye
3
+ - Affogato
4
+ - CaPheSuaDa
5
+ - RedTux
@@ -0,0 +1 @@
1
+ class RedTux < BrewedCoffee
@@ -0,0 +1,6 @@
1
+ COFFEE_MAKER_FILE=Coffeemakerfile.yml coffee-maker --no-milk --verbose --no-ansi 2>&1
2
+ * Brewing BlackEye
3
+ * Brewing Affogato
4
+ * Brewing CaPheSuaDa
5
+ * Brewing RedTux
6
+ * Enjoy!