stringup 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ coverage
3
+ *.gemspec
4
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Bruce Williams
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,112 @@
1
+ Stringup
2
+ ========
3
+
4
+ Quick and dirty test harness for executables, testing assertions
5
+ before and after it is run.
6
+
7
+ Synopsis
8
+ --------
9
+
10
+ Run stringup with:
11
+
12
+ $ stringup execute /path/to/stringup/scenarios/description.rb
13
+
14
+ The description file consists of exactle one `command` line and any
15
+ number of `scenario` definitions; for example, the following file can
16
+ be used to verify running `touch /tmp/foo` will create a new file:
17
+
18
+ command 'touch /tmp/foo'
19
+
20
+ scenario :new, "Creates a file" do
21
+ assert_creates '/tmp/foo'
22
+ end
23
+
24
+ If this was in `scenarios.rb`, you could run this with stringup:
25
+
26
+ $ stringup execute scenarios.rb --scenario new
27
+
28
+ If you had named the scenario `default`, the `--scenario` option
29
+ wouldn't have been necessary, ie:
30
+
31
+ scenario :default "Creates a file" do
32
+ assert_creates '/tmp/foo'
33
+ end
34
+
35
+ $ stringup execute scenarios.rb
36
+
37
+ You'll notice that this should pass the first time and fail on
38
+ subsequent invocations -- because the `assert_creates` fails in the
39
+ event a file exists *before* the command is run.
40
+
41
+ If you want to see what command and scenarios are defined in a file,
42
+ use `stringup info`, eg:
43
+
44
+ $ stringup info scenarios.rb
45
+
46
+ Commands with arguments
47
+ -----------------------
48
+
49
+ Let's say you had an executable that reads in a configuration file and
50
+ has some type of side-effect. You'd like to test running the
51
+ executable against multiple configuration files checking a scenario,
52
+ without having to edit the stringup file every time, changing the path
53
+ to the configuration file.
54
+
55
+ Luckily the command can be provided in `sprintf` style. Assuming our
56
+ executable is named `myexec` and you pass the configuration file to it
57
+ via `-c`, the following would work:
58
+
59
+ command 'myexec -c %s'
60
+
61
+ Now, you just pass more options to `stringup execute`:
62
+
63
+ $ stringup execute scenarios.rb /path/to/my/configuration.conf
64
+
65
+ .. and it's just as if you ran:
66
+
67
+ $ myexec -c /path/to/my/configuration.conf
68
+
69
+ Usage help
70
+ ----------
71
+
72
+ See the commandline help documentation:
73
+
74
+ $ stringup
75
+
76
+ For more information on specific commands, you'll want to use `help`,
77
+ eg:
78
+
79
+ $ stringup help execute
80
+
81
+ Assertions
82
+ ----------
83
+
84
+ The list of assertions is very short at this point.
85
+
86
+ To add an assertion, create a class that inherits from
87
+ `Stringup::Assertion` and implements all the neccessary methods. See
88
+ `Stringup::Assertion` and the currently defined assertions for
89
+ examples.
90
+
91
+ Note: If you put your assertions in `~/.stringup/assertions/*.rb`,
92
+ they'll automatically be loaded. If you create any interesting
93
+ assertions, make sure you let me know!
94
+
95
+ ### assert_creates
96
+
97
+ Checks to see if a file was created.
98
+
99
+ You can pass `:file => true` or `:directory => true` to ensure the
100
+ file is a regular file or directory.
101
+
102
+ ### assert_removes
103
+
104
+ Checks to see if a file was removed.
105
+
106
+ You can pass `:file => true` or `:directory => true` to ensure the
107
+ file was a regular file or directory before being removed.
108
+
109
+ Copyright
110
+ ---------
111
+
112
+ Copyright (c) 2010 Bruce Williams. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "stringup"
8
+ gem.summary = %Q{A test harness for executables}
9
+ gem.description = %Q{An easy-to-use test harness that runs assertions before and after and executable is run}
10
+ gem.email = "bruce@codefluency.com"
11
+ gem.homepage = "http://github.com/bruce/stringup"
12
+ gem.authors = ["Bruce Williams"]
13
+ gem.add_dependency "thor"
14
+ gem.add_dependency "shoulda"
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "stringup #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
data/bin/stringup ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require 'stringup'
6
+
7
+ Stringup::CLI.start
@@ -0,0 +1,3 @@
1
+ file { "/tmp/foo":
2
+ ensure => present
3
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env
2
+
3
+ stringup execute scenarios.rb manifest.pp -s creation
@@ -0,0 +1,5 @@
1
+ command 'puppet %s'
2
+
3
+ scenario :creation, "Creates a file" do
4
+ assert_creates '/tmp/foo'
5
+ end
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env
2
+
3
+ stringup execute scenarios.rb -s creation
@@ -0,0 +1,5 @@
1
+ command 'touch /tmp/foo'
2
+
3
+ scenario :creation, "Creates a file" do
4
+ assert_creates '/tmp/foo'
5
+ end
@@ -0,0 +1,41 @@
1
+ module Stringup
2
+ class Assertion
3
+ extend Enumerable
4
+
5
+ def self.each(&block)
6
+ registry.each(&block)
7
+ end
8
+
9
+ def self.[](name)
10
+ registry[name]
11
+ end
12
+
13
+ def self.registry
14
+ @registry ||= {}
15
+ end
16
+
17
+ def self.assertion(name)
18
+ Assertion.registry[name.to_sym] = self
19
+ end
20
+
21
+ def describe_should_at(event)
22
+ case event
23
+ when :before
24
+ raise NotImplementedError, "Assertion does not describe pre-puppet check"
25
+ when :after
26
+ raise NotImplementedError, "Assertion does not describe post-puppet check"
27
+ else
28
+ raise ArgumentError, "Unknown event #{event.inspect}"
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+
35
+ Dir.glob(File.join(File.dirname(__FILE__), 'assertions', '**/*.rb')) do |path|
36
+ require path
37
+ end
38
+
39
+ Dir.glob(File.join(ENV['HOME'], '.stringup', 'assertions', '**/*.rb')) do |path|
40
+ require path
41
+ end
@@ -0,0 +1,29 @@
1
+ module Stringup
2
+ class FileAssertion < Assertion
3
+
4
+ def initialize(path, options = {})
5
+ @path = path
6
+ @options = options
7
+ end
8
+
9
+ def kind
10
+ if @options[:directory]
11
+ :directory
12
+ elsif @options[:file]
13
+ :file
14
+ end
15
+ end
16
+
17
+ def kind_name
18
+ case kind
19
+ when :file
20
+ 'a regular file'
21
+ when :directory
22
+ 'a directory'
23
+ else
24
+ 'a file'
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ module Stringup
2
+ class FileCreatesAssertion < FileAssertion
3
+ assertion :creates
4
+
5
+ def before(test_case)
6
+ test_case.assert !File.exist?(@path), "File already exists at #{@path}"
7
+ end
8
+
9
+ def after(test_case)
10
+ test_case.assert File.exist?(@path), "File was not created at #{@path}"
11
+ case kind
12
+ when :file
13
+ test_case.assert File.file?(@path), "File created at #{@path} is not a regular file"
14
+ when :directory
15
+ test_case.assert File.file?(@path), "File created at #{@path} is not a directory"
16
+ end
17
+ end
18
+
19
+ def describe_should_at(event)
20
+ case event
21
+ when :before
22
+ "not find #{kind_name} at `#{@path}`"
23
+ when :after
24
+ "have created #{kind_name} at `#{@path}`"
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ module Stringup
2
+ class FileRemovesAssertion < FileAssertion
3
+ assertion :removes
4
+
5
+ def after(test_case)
6
+ test_case.assert !File.exist?(@path), "File still exists at #{@path}"
7
+ end
8
+
9
+ def before(test_case)
10
+ test_case.assert File.exist?(@path), "File does not exist at #{@path}"
11
+ case kind
12
+ when :file
13
+ test_case.assert File.file?(@path), "File to remove at #{@path} is not a regular file"
14
+ when :directory
15
+ test_case.assert File.file?(@path), "File to remove at #{@path} is not a directory"
16
+ end
17
+ end
18
+
19
+ def describe_should_at(event)
20
+ case event
21
+ when :after
22
+ "have removed #{kind_name} at `#{@path}`"
23
+ when :before
24
+ "find #{kind_name} at `#{@path}` to remove"
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ begin
2
+ require 'thor'
3
+ rescue LoadError
4
+ abort "Requires 'thor'"
5
+ end
6
+
7
+ module Stringup
8
+ class CLI < Thor
9
+
10
+ desc "info FILE", "Display defined command and scenarios in FILE"
11
+ def info(path)
12
+ script = path_to_script(path)
13
+ puts "FILE\n\n #{path}\n\n"
14
+ puts "COMMAND\n\n #{script.command}\n\n"
15
+ puts "SCENARIOS\n\n"
16
+ script.scenarios.sort_by { |k, v| k.to_s }.each do |_, scenario|
17
+ puts " #{scenario.name}: #{scenario.description}"
18
+ [:before, :after].each do |event|
19
+ puts " #{event.to_s.upcase}"
20
+ scenario.assertions.each do |assertion|
21
+ puts " should #{assertion.describe_should_at(event)}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ method_option :scenario, :aliases => '-s', :description => "Scenario to run", :default => 'default'
28
+ desc "execute FILE [ARGS_FOR_COMMAND]", "Execute a scenario in FILE"
29
+ def execute(path, *args)
30
+ script = path_to_script(path)
31
+ scenario = script[options[:scenario]]
32
+ unless scenario
33
+ abort "Could not find scenario: #{options[:scenario]}"
34
+ end
35
+ Runner.new(script, scenario, *args).run
36
+ end
37
+
38
+ no_tasks do
39
+ def path_to_script(path)
40
+ unless File.exist?(path)
41
+ abort "No stringup file found at #{path}"
42
+ end
43
+ script = Script.evaluate_file(path)
44
+ script.validate!
45
+ script
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,87 @@
1
+ require 'open3'
2
+
3
+ begin
4
+ require 'shoulda'
5
+ rescue LoadError
6
+ abort "Requires 'shoulda'"
7
+ end
8
+
9
+ begin
10
+ require 'test/unit'
11
+ rescue LoadError
12
+ abort "Requires 'test/unit'. On Ruby 1.9 you may need to install the 'test-unit' gem."
13
+ end
14
+
15
+ require 'test/unit/ui/console/testrunner'
16
+
17
+ module Stringup
18
+ class Runner
19
+
20
+ attr_reader :script, :scenario
21
+ def initialize(script, scenario, *args)
22
+ @script = script
23
+ @scenario = scenario
24
+ @args = args
25
+ @log = ''
26
+ end
27
+
28
+ def run
29
+ Test::Unit.run = false
30
+ suite = Test::Unit::TestSuite.new("#{scenario.description} for `#{command}`")
31
+ suite << test(:before).suite
32
+ suite << test(:after).suite
33
+ Test::Unit::UI::Console::TestRunner.run(suite)
34
+ output_log
35
+ end
36
+
37
+ def output_log
38
+ if @log.empty?
39
+ puts "NO OUTPUT FROM `#{command}`"
40
+ else
41
+ line = "\nOUTPUT FROM `#{command}`"
42
+ puts line, ('=' * line.size)
43
+ puts @log
44
+ end
45
+ end
46
+
47
+ def run_command
48
+ Open3.popen3(command) do |stdin, stdout, stderr|
49
+ stdin.close
50
+ @log << stdout.read
51
+ @log << stderr.read
52
+ end
53
+ true
54
+ end
55
+
56
+ def test(event)
57
+ klass = Class.new(Test::Unit::TestCase) do
58
+ class << self; attr_accessor :stringup, :name, :event; end
59
+ end
60
+ klass.name = "Stringup::Test::Order#{event == :after ? 1 : 0}::#{event.to_s.capitalize}Puppet"
61
+ klass.stringup = self
62
+ klass.event = event
63
+ klass.context "#{event} `#{command}`" do
64
+ if parent.event == :after
65
+ setup do
66
+ @ran_puppet ||= self.class.stringup.run_command
67
+ end
68
+ end
69
+ parent.stringup.scenario.assertions.each do |assertion|
70
+ should assertion.describe_should_at(event) do
71
+ assertion.__send__(event, self)
72
+ end
73
+ end
74
+ end
75
+ klass
76
+ end
77
+
78
+ private
79
+
80
+ def command
81
+ @command ||= @script.command % @args
82
+ rescue ArgumentError
83
+ abort "You provided the wrong number of arguments for command: #{@script.command}"
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,24 @@
1
+ module Stringup
2
+ class Scenario
3
+
4
+ attr_reader :name, :description
5
+ def initialize(name, description = name.to_s.capitalize, &block)
6
+ @name = name
7
+ @description = description
8
+ instance_eval(&block)
9
+ end
10
+
11
+ def assertions
12
+ @assertions ||= []
13
+ end
14
+
15
+ Assertion.each do |name, klass|
16
+ class_eval %{
17
+ def assert_#{name}(*args, &block)
18
+ assertions << Assertion[:#{name}].new(*args, &block)
19
+ end
20
+ }
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ module Stringup
2
+ class Script
3
+
4
+ attr_accessor :command
5
+
6
+ def self.evaluate_file(path)
7
+ evaluate(File.read(path), path)
8
+ end
9
+
10
+ def self.evaluate(text, path)
11
+ script = new
12
+ Builder.new(script).instance_eval(text, path, 1)
13
+ script
14
+ end
15
+
16
+ def <<(scenario)
17
+ scenarios[scenario.name.to_sym] = scenario
18
+ end
19
+
20
+ def scenarios
21
+ @scenarios ||= {}
22
+ end
23
+
24
+ def [](name)
25
+ scenarios[name.to_sym]
26
+ end
27
+
28
+ def validate!
29
+ unless @command
30
+ abort "ERROR: The command was not set.\n\nExample:\n\n command 'cat %s'"
31
+ end
32
+ end
33
+
34
+ class Builder
35
+
36
+ def initialize(script, &block)
37
+ @script = script
38
+ instance_eval(&block) if block_given?
39
+ end
40
+
41
+ def scenario(name, description = name.to_s, &block)
42
+ scenario = ::Stringup::Scenario.new(name, description, &block)
43
+ @script << scenario
44
+ end
45
+ def command(line)
46
+ @script.command = line
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
data/lib/stringup.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Stringup
2
+ autoload :Scenario, 'stringup/scenario'
3
+ autoload :Script, 'stringup/script'
4
+ autoload :CLI, 'stringup/cli'
5
+ autoload :DSL, 'stringup/dsl'
6
+ autoload :Assertion, 'stringup/assertion'
7
+ autoload :FileAssertion, 'stringup/assertions/file_assertion'
8
+ autoload :Runner, 'stringup/runner'
9
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'stringup'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestStringup < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stringup
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
10
+ platform: ruby
11
+ authors:
12
+ - Bruce Williams
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-08 00:00:00 -07:00
18
+ default_executable: stringup
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thor
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: shoulda
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :runtime
43
+ version_requirements: *id002
44
+ description: An easy-to-use test harness that runs assertions before and after and executable is run
45
+ email: bruce@codefluency.com
46
+ executables:
47
+ - stringup
48
+ extensions: []
49
+
50
+ extra_rdoc_files:
51
+ - LICENSE
52
+ - README.markdown
53
+ files:
54
+ - .document
55
+ - .gitignore
56
+ - LICENSE
57
+ - README.markdown
58
+ - Rakefile
59
+ - VERSION
60
+ - bin/stringup
61
+ - examples/puppet/manifest.pp
62
+ - examples/puppet/run.sh
63
+ - examples/puppet/scenarios.rb
64
+ - examples/touch/run.sh
65
+ - examples/touch/scenarios.rb
66
+ - lib/stringup.rb
67
+ - lib/stringup/assertion.rb
68
+ - lib/stringup/assertions/file_assertion.rb
69
+ - lib/stringup/assertions/file_creates_assertion.rb
70
+ - lib/stringup/assertions/file_removes_assertion.rb
71
+ - lib/stringup/cli.rb
72
+ - lib/stringup/runner.rb
73
+ - lib/stringup/scenario.rb
74
+ - lib/stringup/script.rb
75
+ - test/helper.rb
76
+ - test/test_stringup.rb
77
+ has_rdoc: true
78
+ homepage: http://github.com/bruce/stringup
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options:
83
+ - --charset=UTF-8
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ requirements: []
101
+
102
+ rubyforge_project:
103
+ rubygems_version: 1.3.6
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: A test harness for executables
107
+ test_files:
108
+ - test/helper.rb
109
+ - test/test_stringup.rb
110
+ - examples/puppet/scenarios.rb
111
+ - examples/touch/scenarios.rb