stringup 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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