spex 0.4.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,116 @@
1
+ Spex
2
+ ========
3
+
4
+ A quick and dirty test harness for testing assertions before and after
5
+ an executable is run.
6
+
7
+ Synopsis
8
+ --------
9
+
10
+ Spex is a simple language used to define scenarios that model
11
+ the correct behavior of an executable.
12
+
13
+ The description file consists of exactly one `command` line and any
14
+ number of `scenario` definitions; for example, the following file can
15
+ be used to verify running `touch /tmp/foo` will create a new file:
16
+
17
+ command 'touch /tmp/foo'
18
+
19
+ scenario :new, "Creates a file" do
20
+ assert_creates_file '/tmp/foo'
21
+ end
22
+
23
+ If this was in `scenarios.rb`, you could run this with spex:
24
+
25
+ $ spex execute scenarios.rb --scenario new
26
+
27
+ If you had named the scenario `default`, the `--scenario` option
28
+ wouldn't have been necessary, ie:
29
+
30
+ scenario :default "Creates a file" do
31
+ assert_creates_file '/tmp/foo'
32
+ end
33
+
34
+ $ spex execute scenarios.rb
35
+
36
+ You'll notice that this should pass the first time and fail on
37
+ subsequent invocations -- because the `assert_creates` fails in the
38
+ event a file exists *before* the command is run.
39
+
40
+ If you want to see what command and scenarios are defined in a file,
41
+ use `spex info`, eg:
42
+
43
+ $ spex info scenarios.rb
44
+
45
+ Commands with arguments
46
+ -----------------------
47
+
48
+ Let's say you had an executable that reads in a configuration file and
49
+ has some type of side-effect. You'd like to test running the
50
+ executable against multiple configuration files checking a scenario,
51
+ without having to edit the spex file every time, changing the path
52
+ to the configuration file.
53
+
54
+ Luckily the command can be provided in `sprintf` style. Assuming our
55
+ executable is named `myexec` and you pass the configuration file to it
56
+ via `-c`, the following would work:
57
+
58
+ command 'myexec -c %s'
59
+
60
+ Now, you just pass more options to `spex execute`:
61
+
62
+ $ spex execute scenarios.rb /path/to/my/configuration.conf
63
+
64
+ .. and it's just as if you ran:
65
+
66
+ $ myexec -c /path/to/my/configuration.conf
67
+
68
+ Usage help
69
+ ----------
70
+
71
+ See the commandline help documentation:
72
+
73
+ $ spex
74
+
75
+ For more information on specific commands, you'll want to use `help`,
76
+ eg:
77
+
78
+ $ spex help execute
79
+
80
+ Examples
81
+ --------
82
+
83
+ See the `examples/` directory.
84
+
85
+ Assertions
86
+ ----------
87
+
88
+ The list of assertions is very short at this point.
89
+
90
+ To add an assertion, create a class that inherits from
91
+ `Spex::Assertion` and implements all the neccessary methods. See
92
+ `Spex::Assertion` and the currently defined assertions for
93
+ examples.
94
+
95
+ Note: If you put your assertions in `~/.spex/assertions/*.rb`,
96
+ they'll automatically be loaded. If you create any interesting
97
+ assertions, make sure you let me know!
98
+
99
+ ### assert_creates_file
100
+
101
+ Checks to see if a file was created.
102
+
103
+ You can pass `:file => true` or `:directory => true` to ensure the
104
+ file is a regular file or directory.
105
+
106
+ ### assert_removes_file
107
+
108
+ Checks to see if a file was removed.
109
+
110
+ You can pass `:file => true` or `:directory => true` to ensure the
111
+ file was a regular file or directory before being removed.
112
+
113
+ Copyright
114
+ ---------
115
+
116
+ 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 = "spex"
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/spex"
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 = "spex #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
data/bin/spex ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require 'spex'
6
+
7
+ Spex::CLI.start
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ spex execute scenarios.rb
@@ -0,0 +1,5 @@
1
+ command "sudo chgrp everyone /tmp/foo"
2
+
3
+ scenario :default, "Change group" do
4
+ assert_chgrps_file '/tmp/foo', :to => 'everyone', :changes => true
5
+ end
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ spex execute scenarios.rb
@@ -0,0 +1,5 @@
1
+ command "chmod 700 /tmp/foo"
2
+
3
+ scenario :default, "Change mode" do
4
+ assert_chmods_file '/tmp/foo', :to => 0700, :changes => true
5
+ end
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ spex execute scenarios.rb
@@ -0,0 +1,5 @@
1
+ command "sudo chown root /tmp/foo"
2
+
3
+ scenario :default, "Change owner" do
4
+ assert_chowns_file '/tmp/foo', :to => 'root', :changes => true
5
+ end
@@ -0,0 +1,3 @@
1
+ file { "/tmp/foo":
2
+ ensure => present
3
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env
2
+
3
+ spex 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_file '/tmp/foo'
5
+ end
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env
2
+
3
+ spex 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_file '/tmp/foo'
5
+ end
data/lib/spex.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Spex
2
+ autoload :Scenario, 'spex/scenario'
3
+ autoload :Script, 'spex/script'
4
+ autoload :CLI, 'spex/cli'
5
+ autoload :DSL, 'spex/dsl'
6
+ autoload :Assertion, 'spex/assertion'
7
+ autoload :FileAssertion, 'spex/assertions/file_assertion'
8
+ autoload :Runner, 'spex/runner'
9
+ end
@@ -0,0 +1,49 @@
1
+ module Spex
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
+ def before?
33
+ true
34
+ end
35
+
36
+ def after?
37
+ true
38
+ end
39
+
40
+ end
41
+ end
42
+
43
+ Dir.glob(File.join(File.dirname(__FILE__), 'assertions', '**/*.rb')) do |path|
44
+ require path
45
+ end
46
+
47
+ Dir.glob(File.join(ENV['HOME'], '.spex', 'assertions', '**/*.rb')) do |path|
48
+ require path
49
+ end
@@ -0,0 +1,81 @@
1
+ require 'etc'
2
+
3
+ module Spex
4
+ class ChgrpsFileAssertion < FileAssertion
5
+ assertion :chgrps_file
6
+
7
+ def before(test_case)
8
+ if from_groupname
9
+ test_case.assert File.exist?(@path), "File does not exist at #{@path}"
10
+ test_case.assert_equal from_groupname, current_groupname
11
+ elsif @options[:changes]
12
+ test_case.assert_not_equal to_groupname, current_groupname, "Group will not be changed; already '#{to_groupname}'"
13
+ end
14
+ @before_groupname = current_groupname
15
+ end
16
+
17
+ def before?
18
+ from_groupname || @options[:changes]
19
+ end
20
+
21
+ def after(test_case)
22
+ if to_groupname
23
+ test_case.assert File.exist?(@path), "File does not exist at #{@path}"
24
+ test_case.assert_equal to_groupname, current_groupname
25
+ elsif @options[:changes]
26
+ test_case.assert_not_equal @before_groupname, current_groupname, "Group is still '#{@before_groupname}'"
27
+ end
28
+ end
29
+
30
+ def after?
31
+ to_groupname || @options[:changes]
32
+ end
33
+
34
+ def describe_should_at(event)
35
+ case event
36
+ when :before
37
+ if from_groupname
38
+ "change group of file at `#{@path}` from '#{from_groupname}'"
39
+ elsif @options[:changes]
40
+ "not have a file at `#{@path}` with group '#{to_groupname}'"
41
+ end
42
+ when :after
43
+ if to_groupname
44
+ "have changed group of file at `#{@path}` to '#{to_groupname}'"
45
+ elsif @options[:changes]
46
+ "have changed group of file at `#{@path}`"
47
+ end
48
+ else
49
+ super
50
+ end
51
+ end
52
+
53
+ def current_groupname
54
+ normalize(File.stat(@path).gid)
55
+ end
56
+
57
+ def from_groupname
58
+ if @options[:from]
59
+ @from_groupname ||= normalize(@options[:from])
60
+ end
61
+ end
62
+
63
+ def to_groupname
64
+ if @options[:to]
65
+ @to_groupname ||= normalize(@options[:to])
66
+ end
67
+ end
68
+
69
+ def normalize(gid_or_groupname)
70
+ case gid_or_groupname
71
+ when String, Symbol
72
+ gid_or_groupname.to_s
73
+ when Fixnum
74
+ Etc.getgrgid(gid_or_groupname).name
75
+ else
76
+ raise ArgumentError, "Does not appear to be a gid or group name: #{gid_or_groupname}"
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,56 @@
1
+ module Spex
2
+ class ChmodsFileAssertion < FileAssertion
3
+ assertion :chmods_file
4
+
5
+ def before(test_case)
6
+ if @options[:from]
7
+ test_case.assert File.exist?(@path), "File does not exist at #{@path}"
8
+ test_case.assert_equal @options[:from].to_s(8), current_mode.to_s(8)
9
+ elsif @options[:changes]
10
+ test_case.assert_not_equal @options[:to].to_s(8), current_mode.to_s(8), "Mode will not be changed; already at mode #{@options[:to].to_s(8)}"
11
+ end
12
+ @before_mode = current_mode
13
+ end
14
+
15
+ def before?
16
+ @options[:from] || @options[:changes]
17
+ end
18
+
19
+ def after(test_case)
20
+ if @options[:to]
21
+ test_case.assert File.exist?(@path), "File does not exist at #{@path}"
22
+ test_case.assert_equal @options[:to].to_s(8), current_mode.to_s(8)
23
+ elsif @options[:changes]
24
+ test_case.assert_not_equal @before_mode.to_s(8), current_mode.to_s(8), "Mode is still #{@before_mode.to_s(8)}"
25
+ end
26
+ end
27
+
28
+ def after?
29
+ @options[:to] || @options[:changes]
30
+ end
31
+
32
+ def describe_should_at(event)
33
+ case event
34
+ when :before
35
+ if @options[:from]
36
+ "change mode of file at `#{@path}` from #{@options[:from].to_s(8)}"
37
+ elsif @options[:changes]
38
+ "not have a file at `#{@path}` with a mode of #{@options[:to].to_s(8)}"
39
+ end
40
+ when :after
41
+ if @options[:to]
42
+ "change mode of file at `#{@path}` to #{@options[:to].to_s(8)}"
43
+ elsif @options[:changes]
44
+ "change mode of file at `#{@path}`"
45
+ end
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def current_mode
52
+ Integer(File.stat(@path).mode.to_s(8)[1..-1])
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,81 @@
1
+ require 'etc'
2
+
3
+ module Spex
4
+ class ChownsFileAssertion < FileAssertion
5
+ assertion :chowns_file
6
+
7
+ def before(test_case)
8
+ if from_username
9
+ test_case.assert File.exist?(@path), "File does not exist at #{@path}"
10
+ test_case.assert_equal from_username, current_username
11
+ elsif @options[:changes]
12
+ test_case.assert_not_equal to_username, current_username, "Owner will not be changed; already '#{to_username}'"
13
+ end
14
+ @before_username = current_username
15
+ end
16
+
17
+ def before?
18
+ from_username || @options[:changes]
19
+ end
20
+
21
+ def after(test_case)
22
+ if to_username
23
+ test_case.assert File.exist?(@path), "File does not exist at #{@path}"
24
+ test_case.assert_equal to_username, current_username
25
+ elsif @options[:changes]
26
+ test_case.assert_not_equal @before_username, current_username, "Owner is still '#{@before_username}'"
27
+ end
28
+ end
29
+
30
+ def after?
31
+ to_username || @options[:changes]
32
+ end
33
+
34
+ def describe_should_at(event)
35
+ case event
36
+ when :before
37
+ if from_username
38
+ "change owner of file at `#{@path}` from '#{from_username}'"
39
+ elsif @options[:changes]
40
+ "not have a file at `#{@path}` with owner '#{to_username}'"
41
+ end
42
+ when :after
43
+ if to_username
44
+ "have changed owner of file at `#{@path}` to '#{to_username}'"
45
+ elsif @options[:changes]
46
+ "have changed owner of file at `#{@path}`"
47
+ end
48
+ else
49
+ super
50
+ end
51
+ end
52
+
53
+ def current_username
54
+ normalize(File.stat(@path).uid)
55
+ end
56
+
57
+ def from_username
58
+ if @options[:from]
59
+ @from_username ||= normalize(@options[:from])
60
+ end
61
+ end
62
+
63
+ def to_username
64
+ if @options[:to]
65
+ @to_username ||= normalize(@options[:to])
66
+ end
67
+ end
68
+
69
+ def normalize(uid_or_username)
70
+ case uid_or_username
71
+ when String, Symbol
72
+ uid_or_username.to_s
73
+ when Fixnum
74
+ Etc.getpwuid(uid_or_username).name
75
+ else
76
+ raise ArgumentError, "Does not appear to be a uid or username: #{uid_or_username}"
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,31 @@
1
+ module Spex
2
+ class CreatesFileAssertion < FileAssertion
3
+ assertion :creates_file
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,29 @@
1
+ module Spex
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 Spex
2
+ class RemovesFileAssertion < FileAssertion
3
+ assertion :removes_file
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
data/lib/spex/cli.rb ADDED
@@ -0,0 +1,50 @@
1
+ begin
2
+ require 'thor'
3
+ rescue LoadError
4
+ abort "Requires 'thor'"
5
+ end
6
+
7
+ module Spex
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 spex 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,89 @@
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 Spex
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} (`#{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 :spex, :name, :event; end
59
+ end
60
+ klass.name = "Spex::Test::Order#{event == :after ? 1 : 0}::#{event.to_s.capitalize}Puppet"
61
+ klass.spex = self
62
+ klass.event = event
63
+ klass.context "#{event} `#{command}`" do
64
+ if parent.event == :after
65
+ setup do
66
+ @ran_puppet ||= self.class.spex.run_command
67
+ end
68
+ end
69
+ parent.spex.scenario.assertions.each do |assertion|
70
+ if assertion.send("#{event}?")
71
+ should assertion.describe_should_at(event) do
72
+ assertion.__send__(event, self)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ klass
78
+ end
79
+
80
+ private
81
+
82
+ def command
83
+ @command ||= @script.command % @args
84
+ rescue ArgumentError
85
+ abort "You provided the wrong number of arguments for command: #{@script.command}"
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,24 @@
1
+ module Spex
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 Spex
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 = ::Spex::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
@@ -0,0 +1,15 @@
1
+ module Spex
2
+ class Script::Language
3
+
4
+ def initialize(script)
5
+ @script = script
6
+ end
7
+
8
+ def doing(name, description, &block)
9
+ scenario = Scenario.new(name, description)
10
+ Scenario::Builder.new(scenario, &block)
11
+ @script << scenario
12
+ end
13
+
14
+ end
15
+ 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 'spex'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestSpex < 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,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spex
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 4
8
+ - 0
9
+ version: 0.4.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: spex
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
+ - spex
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/spex
61
+ - examples/chgrp/run.sh
62
+ - examples/chgrp/scenarios.rb
63
+ - examples/chmod/run.sh
64
+ - examples/chmod/scenarios.rb
65
+ - examples/chown/run.sh
66
+ - examples/chown/scenarios.rb
67
+ - examples/puppet/manifest.pp
68
+ - examples/puppet/run.sh
69
+ - examples/puppet/scenarios.rb
70
+ - examples/touch/run.sh
71
+ - examples/touch/scenarios.rb
72
+ - lib/spex.rb
73
+ - lib/spex/assertion.rb
74
+ - lib/spex/assertions/chgrps_file_assertion.rb
75
+ - lib/spex/assertions/chmods_file_assertion.rb
76
+ - lib/spex/assertions/chowns_file_assertion.rb
77
+ - lib/spex/assertions/creates_file_assertion.rb
78
+ - lib/spex/assertions/file_assertion.rb
79
+ - lib/spex/assertions/removes_file_assertion.rb
80
+ - lib/spex/cli.rb
81
+ - lib/spex/runner.rb
82
+ - lib/spex/scenario.rb
83
+ - lib/spex/script.rb
84
+ - lib/spex/script/builder.rb
85
+ - test/helper.rb
86
+ - test/test_stringup.rb
87
+ has_rdoc: true
88
+ homepage: http://github.com/bruce/spex
89
+ licenses: []
90
+
91
+ post_install_message:
92
+ rdoc_options:
93
+ - --charset=UTF-8
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ segments:
108
+ - 0
109
+ version: "0"
110
+ requirements: []
111
+
112
+ rubyforge_project:
113
+ rubygems_version: 1.3.6
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: A test harness for executables
117
+ test_files:
118
+ - test/helper.rb
119
+ - test/test_stringup.rb
120
+ - examples/chgrp/scenarios.rb
121
+ - examples/chmod/scenarios.rb
122
+ - examples/chown/scenarios.rb
123
+ - examples/puppet/scenarios.rb
124
+ - examples/touch/scenarios.rb