greybox 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6403c4bd8f174968087fb1add29ea027ffb2e502
4
+ data.tar.gz: eaf7e713f00cc65d609a5c80be3f94479da6d42a
5
+ SHA512:
6
+ metadata.gz: c8ca2cc43f5b623785081aff0918db238fd10e052fe2ac23363dd97539d509afec441fc84b7ddf2c55dd04e7aaf596da0ec21c0c007f60944e0d65f2c6f103c0
7
+ data.tar.gz: 39f8f4521871ee01435b2a1a7829f8037352aaf42d56414e5f6755bcf63942f3e985d3620202dab9c3a06224680bcf8a7fdbda4fe78c031216d2db08407bf739
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ script: bundle exec rake test
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Justin Jaffray
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ [![Build Status](https://travis-ci.org/justinj/greybox.png?branch=master)](https://travis-ci.org/justinj/greybox)
2
+ [![Code Climate](https://codeclimate.com/github/justinj/greybox.png)](https://codeclimate.com/github/justinj/greybox)
3
+
4
+ # Greybox
5
+
6
+ Test a CLI program against a black box.
7
+ Intended for school projects.
8
+
9
+ Often when working on a school project, the class will provide a black box to test your programs against.
10
+ Greybox provides a simple testing harness to easily add new test cases whose expected output is automatically generated by this black box.
11
+ Since these might be slow, or even over an ssh connection, it will store the results so it only has to actually run the black box the first time.
12
+
13
+ ## Installation
14
+
15
+ Only Ruby 1.9.3 and 2.0.0 are supported.
16
+
17
+ $ gem install greybox
18
+
19
+ ## Usage
20
+
21
+ In your project directory, create a file called `Greyfile`.
22
+ A `Greyfile` might look like this:
23
+
24
+ ```ruby
25
+ Greybox.setup do |c|
26
+ c.test_command = "./my_test_command < %"
27
+ c.blackbox_command = "./blackbox_command < %"
28
+ c.input = "test/*.input"
29
+ end
30
+ ```
31
+
32
+ This defines a testing setup in which the command
33
+
34
+ $ ./blackbox_command < INPUT_FILE.input
35
+
36
+ is used as the reference for correctness.
37
+ `%` denotes where the input file will be placed.
38
+
39
+ $ ./my_test_command < INPUT_FILE.input
40
+
41
+ is verified to match the blackbox output every time
42
+
43
+ $ greybox
44
+
45
+ is run.
46
+
47
+ The expected results will be stored in `INPUT_FILE.output`.
48
+ This is the default behaviour but can be changed.
49
+
50
+ As a more complete example, here is a Greyfile I used for a school project:
51
+
52
+ ```ruby
53
+ Greybox.setup do |c|
54
+ c.test_command = "racket wlppscan.ss < % 2>&1"
55
+ c.blackbox_command = "ssh school 'java cs241.WLPPScan' < % 2>&1"
56
+ c.input = "test/*.input"
57
+
58
+ # if the expected ERRORs, all we care about is that
59
+ # the actual ERRORs as well.
60
+ c.comparison = ->(actual, expected) do
61
+ if expected =~ /ERROR/
62
+ actual =~ /ERROR/
63
+ else
64
+ expected == actual
65
+ end
66
+ end
67
+ end
68
+ ```
69
+
70
+ The `test_command` specifies how to run _my_ version of the program, a racket program.
71
+ `blackbox_command` specifies how to generate the expected output for a specific test case.
72
+
73
+ `comparison` specifies how the output of `test_command` should be compared to the black box output.
74
+ In this case, if part of the output contains `ERROR`, all we care about is that the actual output contains `ERROR` as well.
75
+ Otherwise, they must be equal.
76
+
77
+ ## Contributing
78
+
79
+ 1. Fork it
80
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
81
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
82
+ 4. Push to the branch (`git push origin my-new-feature`)
83
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = ["spec/spec_helper"] + FileList['spec/**/*_spec.rb']
7
+ end
8
+
9
+ task :gem do
10
+ sh "yes | gem uninstall greybox; true"
11
+ sh "gem build greybox.gemspec"
12
+ sh "gem install *.gem"
13
+ end
data/bin/greybox ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "greybox"
4
+
5
+ begin
6
+ load Dir.pwd + "/Greyfile"
7
+ rescue LoadError
8
+ puts "Could not load Greyfile"
9
+ end
data/example/Greyfile ADDED
@@ -0,0 +1,5 @@
1
+ Greybox.setup do |c|
2
+ c.input "test/*.input"
3
+ c.blackbox_command "cat < %"
4
+ c.test_command "cat < %"
5
+ end
@@ -0,0 +1 @@
1
+ Hello
@@ -0,0 +1 @@
1
+ Hello
data/greybox.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'greybox/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "greybox"
8
+ spec.version = Greybox::VERSION
9
+ spec.authors = ["Justin Jaffray"]
10
+ spec.email = ["justin.jaffray@gmail.com"]
11
+ spec.description = %q{Test against a black box.}
12
+ spec.summary = %q{Easily test school assignments or other projects where you have a reference}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "diffy"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "fakefs"
26
+ spec.add_development_dependency "mocha"
27
+ end
@@ -0,0 +1,66 @@
1
+ module Greybox
2
+ module Configurable
3
+ attr_accessor :properties
4
+
5
+ module ClassMethods
6
+ attr_accessor :defaults
7
+ attr_accessor :required_properties
8
+ def def_property(name, args = {})
9
+ setter = "#{name}=".to_sym
10
+ getter = name
11
+ (@defaults ||= {})[name] = args[:default] if args.has_key? :default
12
+ (@required_properties ||= Set.new) << name if args[:required]
13
+
14
+ # Stolen from RubyTapas episodes 27 and 28
15
+ mod = if const_defined?(:Properties, false)
16
+ const_get(:Properties)
17
+ else
18
+ const_set(:Properties, new_blank_properties_module)
19
+ end
20
+
21
+ mod.module_eval %Q{
22
+ def #{setter}(value)
23
+ @properties ||= {}
24
+ @properties[:#{name}] = value
25
+ end
26
+
27
+ def #{getter}
28
+ get_prop(:#{name})
29
+ end
30
+ }
31
+
32
+ include mod
33
+ end
34
+
35
+ private
36
+ def new_blank_properties_module
37
+ Module.new do
38
+ class << self
39
+ def to_s
40
+ "Properties(#{properties.join(", ")})"
41
+ end
42
+ alias_method :inspect, :to_s
43
+
44
+ def properties
45
+ instance_methods(false).reject { |m| m.to_s.end_with? "=" }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def get_prop(prop)
53
+ (@properties || {}).fetch(prop, self.class.defaults[prop])
54
+ end
55
+
56
+ def verify
57
+ self.class.required_properties.each do |prop|
58
+ raise "Property #{prop} is required." unless get_prop(prop)
59
+ end
60
+ end
61
+
62
+ def self.included(base)
63
+ base.extend(ClassMethods)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,12 @@
1
+ module Greybox
2
+ class Configuration
3
+ include Configurable
4
+
5
+ def_property :input, required: true
6
+ def_property :test_command, required: true
7
+ def_property :expected, default: ->(input) { input.gsub(/\.input$/, ".output") }
8
+ def_property :comparison, default: ->(actual, expected) { actual == expected }
9
+ def_property :blackbox_command, required: true
10
+
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ module Greybox
2
+ class Runner
3
+ attr_reader :failures
4
+ attr_reader :config
5
+
6
+ def initialize(config)
7
+ @config = config
8
+ @failures = []
9
+ end
10
+ def files
11
+ Dir.glob(config.input)
12
+ end
13
+
14
+ def expected_filename(input_filename)
15
+ expected_filename = config.expected.call(input_filename)
16
+ raise "Output filename for #{input_filename} was same as input!" if expected_filename == input_filename
17
+ expected_filename
18
+ end
19
+
20
+ def expected_for(filename)
21
+ file = expected_filename(filename)
22
+ File.open(file, 'w') do |f|
23
+ f.write run_command(config.blackbox_command, filename)
24
+ end
25
+ File.read(file)
26
+ end
27
+
28
+ def run
29
+ files.each do |input_file|
30
+ expected = expected_for(input_file)
31
+ actual = run_command(config.test_command, input_file)
32
+ report_failure(input_file, expected, actual) unless config.comparison.call(actual, expected)
33
+ end
34
+ end
35
+
36
+ def run_command(command, file)
37
+ `#{insert_filename(command, file)}`
38
+ end
39
+
40
+ def insert_filename(command, filename)
41
+ command.gsub("%", filename)
42
+ end
43
+
44
+ def report_failure(filename, expected, actual)
45
+ @failures << { filename: filename, expected: expected, actual: actual }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module Greybox
2
+ VERSION = "0.0.1"
3
+ end
data/lib/greybox.rb ADDED
@@ -0,0 +1,38 @@
1
+ require "set"
2
+ require "diffy"
3
+ require "greybox/version"
4
+ require "greybox/configurable"
5
+ require "greybox/runner"
6
+ require "greybox/configuration"
7
+
8
+ module Greybox
9
+ class << self
10
+ attr_reader :failures
11
+ attr_reader :configuration
12
+ attr_reader :runner
13
+ def setup(&blk)
14
+ config(&blk)
15
+ run
16
+ check
17
+ end
18
+
19
+ def run
20
+ @runner = Runner.new(configuration)
21
+ runner.run
22
+ end
23
+
24
+ def check
25
+ runner.failures.each do |failure|
26
+ puts "FAILED:"
27
+ puts "filename: #{failure[:filename]}"
28
+ puts Diffy::Diff.new(failure[:expected], failure[:actual])
29
+ end
30
+ end
31
+
32
+ def config
33
+ @configuration = Configuration.new
34
+ yield configuration
35
+ configuration.verify
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,12 @@
1
+ module Greybox
2
+ describe Greybox do
3
+ describe "errors" do
4
+ it "complains if a missing property is gone before being run" do
5
+ -> do
6
+ Greybox.config do |c|
7
+ end
8
+ end.must_raise RuntimeError
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,108 @@
1
+ module Greybox
2
+ describe Runner do
3
+ before do
4
+ FakeFS.activate!
5
+
6
+ FileUtils.touch "inputfile.input"
7
+ end
8
+
9
+ after do
10
+ FakeFS.activate!
11
+ Dir.glob("*").each { |f| FileUtils.rm f }
12
+ FakeFS.deactivate!
13
+ end
14
+
15
+ # it should be noted these are just the defaults
16
+ # for these tests to reduce duplication,
17
+ # the real-world defaults are different
18
+ def defaults
19
+ {
20
+ input: "*.input",
21
+ expected: ->(input) { "output_file" },
22
+ blackbox_command: "echo BLACKBOX_COMMAND",
23
+ comparison: ->(actual, expected) { actual == expected },
24
+ }
25
+ end
26
+
27
+ attr_reader :runner
28
+
29
+ def with_config(config = {})
30
+ @runner = Runner.new(stub(defaults.merge(config)))
31
+ end
32
+
33
+ describe "finding files" do
34
+ it "uses the glob to find files" do
35
+ with_config
36
+ runner.files.must_equal ["inputfile.input"]
37
+ end
38
+
39
+ it "uses the 'expected' value to determine the expected name" do
40
+ with_config(expected: ->(input) {
41
+ input.gsub(/\.input$/, ".output")
42
+ })
43
+ runner.expected_filename("hello.input").must_equal "hello.output"
44
+ end
45
+
46
+ it "complains if the output is the same as the input" do
47
+ with_config(expected: ->(input){ input })
48
+ ->() { runner.expected_filename("input.input") }.must_raise RuntimeError
49
+ end
50
+ end
51
+
52
+ describe "running commands" do
53
+ it "uses the provided test command" do
54
+ with_config(test_command: "echo TEST_COMMAND")
55
+ runner.expects(:`).with("echo TEST_COMMAND")
56
+ runner.expects(:`).with("echo BLACKBOX_COMMAND")
57
+ runner.run
58
+ end
59
+
60
+ it "inserts filenames for %'s" do
61
+ with_config(test_command: "echo hi %")
62
+ runner.expects(:`).with "echo hi inputfile.input"
63
+ runner.expects(:`).with "echo BLACKBOX_COMMAND"
64
+ runner.run
65
+ end
66
+
67
+ it "uses the provided blackbox command to find the expected value for cases that don't have one yet" do
68
+ with_config(test_command: "echo hi",
69
+ blackbox_command: "echo output")
70
+ runner.expects(:`).with "echo hi"
71
+ runner.expects(:`).with "echo output"
72
+ runner.run
73
+ end
74
+
75
+ it "inserts output filenames for %'s in the blackbox command" do
76
+ with_config(test_command: "echo hi",
77
+ blackbox_command: "echo output < %")
78
+ runner.expects(:`).with "echo hi"
79
+ runner.expects(:`).with "echo output < inputfile.input"
80
+ runner.run
81
+ end
82
+ end
83
+
84
+ describe "asserting output" do
85
+ it "compares for equality by default" do
86
+ with_config(test_command: "echo hi",
87
+ blackbox_command: "echo hi")
88
+ runner.run
89
+ runner.failures.must_be_empty
90
+ end
91
+
92
+ it "has a failure if a test fails" do
93
+ with_config(test_command: "echo hi",
94
+ blackbox_command: "echo hello")
95
+ runner.run
96
+ runner.failures.wont_be_empty
97
+ end
98
+
99
+ it "compares by the provided comparison" do
100
+ with_config(comparison: ->(actual, expected) { actual == "hi\n" },
101
+ test_command: "echo hi",
102
+ blackbox_command: "echo hello")
103
+ runner.run
104
+ runner.failures.must_be_empty
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,5 @@
1
+ require "minitest/autorun"
2
+ require "mocha/setup"
3
+ require "fakefs"
4
+ require "fileutils"
5
+ require_relative "../lib/greybox"
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: greybox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Justin Jaffray
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: diffy
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: fakefs
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mocha
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Test against a black box.
84
+ email:
85
+ - justin.jaffray@gmail.com
86
+ executables:
87
+ - greybox
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - .travis.yml
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/greybox
98
+ - example/Greyfile
99
+ - example/test/file1.input
100
+ - example/test/file1.output
101
+ - greybox.gemspec
102
+ - lib/greybox.rb
103
+ - lib/greybox/configurable.rb
104
+ - lib/greybox/configuration.rb
105
+ - lib/greybox/runner.rb
106
+ - lib/greybox/version.rb
107
+ - spec/greybox_spec.rb
108
+ - spec/runner_spec.rb
109
+ - spec/spec_helper.rb
110
+ homepage: ''
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project:
130
+ rubygems_version: 2.0.3
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Easily test school assignments or other projects where you have a reference
134
+ test_files:
135
+ - spec/greybox_spec.rb
136
+ - spec/runner_spec.rb
137
+ - spec/spec_helper.rb