spex 0.4.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,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