clintegracon 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +158 -0
- data/Rakefile +60 -0
- data/clintegracon.gemspec +34 -0
- data/lib/CLIntegracon.rb +9 -0
- data/lib/CLIntegracon/adapter/bacon.rb +208 -0
- data/lib/CLIntegracon/configuration.rb +67 -0
- data/lib/CLIntegracon/diff.rb +81 -0
- data/lib/CLIntegracon/file_tree_spec.rb +213 -0
- data/lib/CLIntegracon/file_tree_spec_context.rb +180 -0
- data/lib/CLIntegracon/formatter.rb +77 -0
- data/lib/CLIntegracon/subject.rb +128 -0
- data/lib/CLIntegracon/version.rb +3 -0
- data/spec/bacon/execution_output.txt +72 -0
- data/spec/bacon/spec_helper.rb +60 -0
- data/spec/fixtures/bin/coffeemaker.rb +58 -0
- data/spec/integration/coffeemaker_help/after/execution_output.txt +23 -0
- data/spec/integration/coffeemaker_help/before/.gitkeep +0 -0
- data/spec/integration/coffeemaker_no_milk/after/BlackEye.brewed-coffee +1 -0
- data/spec/integration/coffeemaker_no_milk/after/CaPheSuaDa.brewed-coffee +1 -0
- data/spec/integration/coffeemaker_no_milk/after/Coffeemakerfile.yml +5 -0
- data/spec/integration/coffeemaker_no_milk/after/RedTux.brewed-coffee +1 -0
- data/spec/integration/coffeemaker_no_milk/after/execution_output.txt +6 -0
- data/spec/integration/coffeemaker_no_milk/before/Coffeemakerfile.yml +5 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/Affogato.brewed-coffee +2 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/BlackEye.brewed-coffee +2 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/Coffeemakerfile.yml +3 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/RedTux.brewed-coffee +2 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/execution_output.txt +4 -0
- data/spec/integration/coffeemaker_sweetner_honey/before/Coffeemakerfile.yml +3 -0
- data/spec/unit/adapter/bacon_spec.rb +187 -0
- data/spec/unit/configuration_spec.rb +72 -0
- 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,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
|
+
* [32mAffogato.brewed-coffee[0m
|
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
|
+
[32m+ @milk = true[0m
|
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
|
+
* [31mBlackEye.brewed-coffee[0m
|
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
|
+
[31m- @sweetner = sugar[0m
|
61
|
+
[32m+ @milk = true[0m
|
62
|
+
[32m+ @sweetner = honey[0m
|
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>'
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
class BlackEye < BrewedCoffee
|
@@ -0,0 +1 @@
|
|
1
|
+
class CaPheSuaDa < BrewedCoffee
|
@@ -0,0 +1 @@
|
|
1
|
+
class RedTux < BrewedCoffee
|