pig-spec 0.0.2
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +56 -0
- data/Rakefile +1 -0
- data/lib/pig-spec.rb +6 -0
- data/lib/pig-spec/pig_unit_core.rb +130 -0
- data/lib/pig-spec/version.rb +3 -0
- data/pig-spec.gemspec +20 -0
- data/spec/pig-spec/pig_unit_core_spec.rb +379 -0
- data/spec/spec_helper.rb +4 -0
- metadata +89 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Summary/Description
|
2
|
+
===================
|
3
|
+
PigSpec is a Ruby gem which can be used to test Pig scripts. It was designed to be easily integrated with the RSpec framework. It is loosely based on PigUnit (see: https://pig.apache.org/docs/r0.8.1/pigunit.html).
|
4
|
+
|
5
|
+
Building the gem:
|
6
|
+
-----------------
|
7
|
+
The PigSpec gem can be built with the following command:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
gem build pig-spec.gemspec
|
11
|
+
```
|
12
|
+
|
13
|
+
Example usage:
|
14
|
+
--------------
|
15
|
+
```ruby
|
16
|
+
require 'pig-spec'
|
17
|
+
|
18
|
+
describe 'something to be tested' do
|
19
|
+
include PigSpec
|
20
|
+
|
21
|
+
it 'should run the pig script and produce a single line of output' do
|
22
|
+
test_pig_script 'pig-0.6.0-core.jar', 'fake_script.pig', { "generated_file.txt" => "PigSpec creates this file. Current time is: #{Time.now}" }, { "output.csv" => "1,2,3,4" }, { "param1" => "foo", "param2" => "bar" }
|
23
|
+
verify_output(false).should == true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
If PigSpec is properly installed, you will see the following line (along with standard RSpec output) when the spec is run:
|
29
|
+
|
30
|
+
>Running the following command: java -jar pig-0.6.0-core.jar -x local -p param1=foo -p param2=bar fake_script.pig
|
31
|
+
|
32
|
+
Unless you've made some modifications to the above example (e.g. pointing it to an actual Pig JAR & script on your system), the above example will fail and produce output that looks like:
|
33
|
+
|
34
|
+
>Running the following command: java -jar pig-0.6.0-core.jar -x local -p param1=foo -p param2=bar fake_script.pig
|
35
|
+
>Unable to access jarfile pig-0.6.0-core.jar
|
36
|
+
>Pig script exited with non-zero exit code: 256.
|
37
|
+
>\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
|
38
|
+
>| Verifying Pig script output... |
|
39
|
+
>\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
|
40
|
+
>Pig script exited with non-zero exit code: 256.
|
41
|
+
>F
|
42
|
+
>
|
43
|
+
>Failures:
|
44
|
+
>
|
45
|
+
> 1) something to be tested should run the pig script and produce a single line of output
|
46
|
+
> Failure/Error: verify_output(false).should == true
|
47
|
+
> expected: true
|
48
|
+
> got: false (using ==)
|
49
|
+
> # ./spec/example_spec.rb:8
|
50
|
+
>
|
51
|
+
>Finished in 0.0131 seconds
|
52
|
+
>1 example, 1 failure
|
53
|
+
>
|
54
|
+
>Failed examples:
|
55
|
+
>
|
56
|
+
>rspec ./spec/example_spec.rb:6 # something to be tested should run the pig script and produce a single line of output
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/pig-spec.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
module PigSpec
|
2
|
+
require 'fileutils'
|
3
|
+
INPUT_DIR_PREFIX = "pig_test_"
|
4
|
+
PIG_CMD_PREFIX = "java -jar "
|
5
|
+
|
6
|
+
attr_reader :output_files, :input_dir, :error_stream, :stdout_stream, :exit_code
|
7
|
+
@@test_number = 0
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@output_files = {}
|
11
|
+
@input_dir = ""
|
12
|
+
@error_stream = $stderr
|
13
|
+
@stdout_stream = $stdout
|
14
|
+
@exit_code = 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.test_number
|
18
|
+
@@test_number
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_pig_script(pig_binary, pig_script, input_files_hash, output_files_hash, params)
|
22
|
+
@error_stream = error_stream
|
23
|
+
@stdout_stream = stdout_stream
|
24
|
+
@@test_number = @@test_number + 1
|
25
|
+
|
26
|
+
write_input_files(input_files_hash)
|
27
|
+
@output_files = output_files_hash
|
28
|
+
run_script(pig_binary, pig_script, params)
|
29
|
+
end
|
30
|
+
|
31
|
+
def build_pig_script_params(params)
|
32
|
+
if params.class != Hash
|
33
|
+
error_stream.puts "Params had unexpected class: #{params.class}"
|
34
|
+
return ""
|
35
|
+
end
|
36
|
+
|
37
|
+
params.reduce("") do |param_str, param|
|
38
|
+
param_str + (param_str.empty? ? "" : " ") + "-p " + param[0].to_s + "=" + param[1].to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_cmd_line(pig_binary, pig_script, params)
|
43
|
+
pig_params = build_pig_script_params(params)
|
44
|
+
pig_params = pig_params.empty? ? "" : "#{pig_params} "
|
45
|
+
"#{PIG_CMD_PREFIX}#{pig_binary} -x local #{pig_params}#{pig_script}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_script(pig_binary, pig_script, params)
|
49
|
+
cmd_to_run = build_cmd_line(pig_binary, pig_script, params)
|
50
|
+
stdout_stream.puts("Running the following command: #{cmd_to_run}")
|
51
|
+
original_cwd = Dir.pwd
|
52
|
+
Dir.chdir(input_dir)
|
53
|
+
system cmd_to_run
|
54
|
+
@exit_code = $?
|
55
|
+
if exit_code != 0
|
56
|
+
error_stream.puts "Pig script exited with non-zero exit code: #{exit_code}."
|
57
|
+
end
|
58
|
+
Dir.chdir(original_cwd)
|
59
|
+
end
|
60
|
+
|
61
|
+
def write_input_files(input_files_hash)
|
62
|
+
@input_dir = File.expand_path(INPUT_DIR_PREFIX + PigSpec.test_number.to_i.to_s)
|
63
|
+
|
64
|
+
FileUtils.rm_rf(input_dir)
|
65
|
+
FileUtils.mkdir_p(input_dir)
|
66
|
+
|
67
|
+
if input_files_hash.class != Hash
|
68
|
+
error_stream.puts "Input files had unexpected class: #{input_files_hash.class}"
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
input_files_hash.keys.each do |file_name|
|
73
|
+
File.open(File.join(input_dir, file_name.to_s), "w") { |f| f.print(input_files_hash[file_name]) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def print_mismatch_error(file)
|
78
|
+
error_stream.puts "Mismatch detected in '#{file}':"
|
79
|
+
end
|
80
|
+
|
81
|
+
def compare_pairs(file, pairs, mapping)
|
82
|
+
pairs.each do |pair|
|
83
|
+
if pair[0] != pair[1]
|
84
|
+
print_mismatch_error(file)
|
85
|
+
error_stream.puts "\tExpected line: '#{pair[mapping[:expected]]}'"
|
86
|
+
error_stream.puts "\tActual line: '#{pair[mapping[:actual]]}'"
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def verify_output(order_matters)
|
93
|
+
stdout_stream.puts "----------------------------------"
|
94
|
+
stdout_stream.puts "| Verifying Pig script output... |"
|
95
|
+
stdout_stream.puts "----------------------------------"
|
96
|
+
if output_files.class != Hash
|
97
|
+
error_stream.puts "Expected hash of expected output with (filename, file content) pairs. Unexpected class: #{output_files.class}"
|
98
|
+
return false
|
99
|
+
elsif output_files.size == 0
|
100
|
+
error_stream.puts "No output files to verify."
|
101
|
+
return false
|
102
|
+
end
|
103
|
+
|
104
|
+
if exit_code != 0
|
105
|
+
error_stream.puts "Pig script exited with non-zero exit code: #{exit_code}."
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
|
109
|
+
all_output_matched = true
|
110
|
+
original_cwd = Dir.pwd
|
111
|
+
Dir.chdir(input_dir)
|
112
|
+
|
113
|
+
output_files.keys.each do |file|
|
114
|
+
file_lines_array = IO.read(file).split("\n")
|
115
|
+
file_lines_array = file_lines_array.sort if !order_matters
|
116
|
+
|
117
|
+
expected_output_array = output_files[file].split("\n")
|
118
|
+
expected_output_array = expected_output_array.sort if !order_matters
|
119
|
+
|
120
|
+
if (!compare_pairs(file, expected_output_array.zip(file_lines_array), { :expected => 0, :actual => 1 }) ||
|
121
|
+
!compare_pairs(file, file_lines_array.zip(expected_output_array), { :expected => 1, :actual => 0 }))
|
122
|
+
all_output_matched = false
|
123
|
+
next
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
Dir.chdir(original_cwd)
|
128
|
+
all_output_matched
|
129
|
+
end
|
130
|
+
end
|
data/pig-spec.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "pig-spec/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "pig-spec"
|
7
|
+
s.version = PigSpec::VERSION
|
8
|
+
s.authors = ["Matt Martin"]
|
9
|
+
s.email = ["matt [dot] martin [at] thinkbiganalytics [dot] com"]
|
10
|
+
s.homepage = "http://www.thinkbiganalytics.com/"
|
11
|
+
s.summary = %q{PigSpec is a PigUnit-like program implemented in Ruby.}
|
12
|
+
s.description = %q{PigSpec is a PigUnit-like program implemented in Ruby. It can be easily included into RSpec for running integration tests of existing Pig scripts.}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_development_dependency "rspec"
|
20
|
+
end
|
@@ -0,0 +1,379 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pig-spec/pig_unit_core'
|
3
|
+
|
4
|
+
class DummyPigSpec
|
5
|
+
include PigSpec
|
6
|
+
end
|
7
|
+
|
8
|
+
describe PigSpec do
|
9
|
+
subject { DummyPigSpec.new }
|
10
|
+
|
11
|
+
let(:pig_binary) { "pig-0.6.0-core.jar" }
|
12
|
+
let(:script_name) { "test.pig" }
|
13
|
+
let(:input_hash) { {"foo.txt" => "bar"} }
|
14
|
+
let(:output_hash) { {:foo => "baz"} }
|
15
|
+
let(:params) { {:param1 => "baz"} }
|
16
|
+
let(:unique_spec_dir) { PigSpec::INPUT_DIR_PREFIX.to_s + PigSpec.test_number.to_s }
|
17
|
+
let(:mock_stdout) { mock }
|
18
|
+
let(:mock_stderr) { mock }
|
19
|
+
|
20
|
+
before do
|
21
|
+
# Stub out stdout and stderr for tests where they are not relevant
|
22
|
+
subject.stub(:stdout_stream => mock_stdout)
|
23
|
+
mock_stdout.stub(:puts)
|
24
|
+
|
25
|
+
subject.stub(:error_stream => mock_stderr)
|
26
|
+
mock_stderr.stub(:puts)
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_unique_str
|
30
|
+
Time.now.to_f.to_s.gsub(".", "_")
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#test_pig_script' do
|
34
|
+
before do
|
35
|
+
subject.stub(:write_input_files)
|
36
|
+
subject.stub(:run_script)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should call the run_script method with the appropriate command line parameters" do
|
40
|
+
subject.should_receive(:run_script).with(pig_binary, script_name, params)
|
41
|
+
subject.test_pig_script(pig_binary, script_name, input_hash, output_hash, params)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should call the write_input_files method" do
|
45
|
+
subject.should_receive(:write_input_files).with(input_hash)
|
46
|
+
subject.test_pig_script(pig_binary, script_name, input_hash, output_hash, params)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should store the expected output files in an instance variable" do
|
50
|
+
subject.test_pig_script(pig_binary, script_name, input_hash, output_hash, params)
|
51
|
+
subject.output_files.should == output_hash
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should increment the test number" do
|
55
|
+
prev_test_number = PigSpec.test_number
|
56
|
+
subject.test_pig_script(pig_binary, script_name, input_hash, output_hash, params)
|
57
|
+
PigSpec.test_number.should == prev_test_number + 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#write_input_files' do
|
62
|
+
before do
|
63
|
+
PigSpec.stub(:test_number).and_return(Time.now.to_i)
|
64
|
+
FileUtils.rm_rf(unique_spec_dir) if File.exists?(unique_spec_dir)
|
65
|
+
end
|
66
|
+
|
67
|
+
after do
|
68
|
+
FileUtils.rm_rf(unique_spec_dir) if File.exists?(unique_spec_dir)
|
69
|
+
end
|
70
|
+
|
71
|
+
let(:input_files) { {} }
|
72
|
+
|
73
|
+
context "when a temporary output directory does not already exist" do
|
74
|
+
it "should create a temporary directory using the test number" do
|
75
|
+
subject.write_input_files(input_files)
|
76
|
+
File.directory?(unique_spec_dir).should == true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when a temporary output directory already exists" do
|
81
|
+
before do
|
82
|
+
FileUtils.mkdir_p(unique_spec_dir)
|
83
|
+
File.open(File.join(unique_spec_dir, generate_unique_str + ".txt"), "w") { |f| f.print("hello world") }
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should delete the existing directory contents" do
|
87
|
+
Dir.glob(File.join(unique_spec_dir, "*")).size.should > 0
|
88
|
+
subject.write_input_files(input_files)
|
89
|
+
Dir.glob(File.join(unique_spec_dir, "*")).size.should == 0
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when the hash of input files is not empty" do
|
94
|
+
let(:input_files) { { "foo_#{generate_unique_str}.txt" => generate_unique_str,
|
95
|
+
"bar_#{generate_unique_str}.txt" => generate_unique_str } }
|
96
|
+
|
97
|
+
it "should create one file per hash key and use the hash keys as the filename and hash values as the content" do
|
98
|
+
subject.write_input_files(input_files)
|
99
|
+
|
100
|
+
input_files.keys.each do |file_name|
|
101
|
+
file_path = File.join(unique_spec_dir, file_name)
|
102
|
+
File.exists?(file_path).should == true
|
103
|
+
IO.readlines(file_path).join("").should == input_files[file_name]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "when the paramter is not a hash" do
|
109
|
+
let(:input_files) { nil }
|
110
|
+
before do
|
111
|
+
input_files.class.should_not == Hash
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should print an error and return an empty string" do
|
115
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
116
|
+
mock_stderr.should_receive(:puts).with("Input files had unexpected class: #{input_files.class}")
|
117
|
+
|
118
|
+
subject.write_input_files(input_files)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#build_pig_script_params' do
|
124
|
+
context "when at least one paramter exists" do
|
125
|
+
let(:params) { {"foo" => 123, "bAr" => "234"} }
|
126
|
+
it "should create a string where the hash keys are the parameter names and the parameter values are the hash values" do
|
127
|
+
subject.build_pig_script_params(params).should == "-p foo=123 -p bAr=234"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "when the hash of paramters is empty" do
|
132
|
+
let(:params) { {} }
|
133
|
+
it "should return an empty string" do
|
134
|
+
subject.build_pig_script_params(params).should == ""
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "when the paramter is not a hash" do
|
139
|
+
let(:params) { nil }
|
140
|
+
before do
|
141
|
+
params.class.should_not == Hash
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should print an error and return an empty string" do
|
145
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
146
|
+
mock_stderr.should_receive(:puts).with("Params had unexpected class: #{params.class}")
|
147
|
+
|
148
|
+
subject.build_pig_script_params(params).should == ""
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe '#build_cmd_line' do
|
154
|
+
it "should call the build_pig_script_params helper method" do
|
155
|
+
subject.should_receive(:build_pig_script_params).with(params).and_return("-p fake=fake_value")
|
156
|
+
subject.build_cmd_line(pig_binary, script_name, params)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should build a string which joins a standard prefix with the flattened pig parameters and pig script name" do
|
160
|
+
subject.build_cmd_line(pig_binary, script_name, params).should == "#{PigSpec::PIG_CMD_PREFIX}#{pig_binary} -x local -p param1=baz #{script_name}"
|
161
|
+
end
|
162
|
+
|
163
|
+
context "when there are no Pig script parameters" do
|
164
|
+
let(:params) { {} }
|
165
|
+
it "should build a script which joins a standard prefix and the pig script name" do
|
166
|
+
subject.build_cmd_line(pig_binary, script_name, params).should == "#{PigSpec::PIG_CMD_PREFIX}#{pig_binary} -x local #{script_name}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#run_script' do
|
172
|
+
before do
|
173
|
+
Dir.stub(:chdir)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should call the build_cmd_line helper method" do
|
177
|
+
subject.should_receive(:build_cmd_line).with(pig_binary, script_name, params).and_return("echo 'echo' is a harmless command")
|
178
|
+
subject.run_script(pig_binary, script_name, params)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should print the command that it is running to stdout" do
|
182
|
+
subject.stub(:build_cmd_line => 'echo')
|
183
|
+
mock_stdout.should_receive(:puts).with("Running the following command: echo")
|
184
|
+
subject.run_script(pig_binary, script_name, params)
|
185
|
+
end
|
186
|
+
|
187
|
+
def echo_string_to_unique_file(base_dir)
|
188
|
+
unique_str = generate_unique_str
|
189
|
+
unique_filename = File.join(base_dir, "temp_" + unique_str + ".txt")
|
190
|
+
FileUtils.rm_rf(unique_filename) if File.exists?(unique_filename)
|
191
|
+
|
192
|
+
subject.stub(:build_cmd_line => 'echo "' + unique_str + '" > ' + unique_filename)
|
193
|
+
|
194
|
+
subject.run_script(pig_binary, script_name, params)
|
195
|
+
File.exists?(unique_filename).should == true
|
196
|
+
IO.readlines(unique_filename).join("") == unique_str
|
197
|
+
|
198
|
+
FileUtils.rm_rf(unique_filename)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should run the string returned by the build_cmd_line helper method" do
|
202
|
+
unique_filename = echo_string_to_unique_file(File.expand_path("."))
|
203
|
+
end
|
204
|
+
|
205
|
+
context "when the command fails" do
|
206
|
+
it "should print an error message with the appropriate exit code" do
|
207
|
+
subject.stub(:build_cmd_line => 'false')
|
208
|
+
system 'false'
|
209
|
+
false_exit_code = $?
|
210
|
+
|
211
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
212
|
+
mock_stderr.should_receive(:puts).with("Pig script exited with non-zero exit code: #{false_exit_code}.")
|
213
|
+
|
214
|
+
subject.exit_code.should == 0
|
215
|
+
subject.run_script(pig_binary, script_name, params)
|
216
|
+
subject.exit_code.should == false_exit_code
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should run the command within the temporary directory created for this test" do
|
221
|
+
original_cwd = Dir.pwd
|
222
|
+
unique_dir = File.expand_path(FileUtils.mkdir_p(generate_unique_str))
|
223
|
+
subject.should_receive(:input_dir).and_return(unique_dir)
|
224
|
+
|
225
|
+
unique_filename = echo_string_to_unique_file(unique_dir)
|
226
|
+
Dir.pwd.should == original_cwd
|
227
|
+
|
228
|
+
FileUtils.rm_rf(unique_dir)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe '#verify_output' do
|
233
|
+
let(:order_matters) { true }
|
234
|
+
|
235
|
+
it "should print a standard message letting the user know that it's about to start verifying output" do
|
236
|
+
mock_stdout.should_receive(:puts).with("----------------------------------")
|
237
|
+
mock_stdout.should_receive(:puts).with("| Verifying Pig script output... |")
|
238
|
+
mock_stdout.should_receive(:puts).with("----------------------------------")
|
239
|
+
subject.verify_output(order_matters)
|
240
|
+
end
|
241
|
+
|
242
|
+
context "when there is no expected output" do
|
243
|
+
before do
|
244
|
+
subject.stub(:output_files).and_return( {} )
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should print an error message and return false" do
|
248
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
249
|
+
mock_stderr.should_receive(:puts).with("No output files to verify.")
|
250
|
+
subject.verify_output(order_matters).should == false
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context "when the expected output is not a hash" do
|
255
|
+
before do
|
256
|
+
subject.stub(:output_files).and_return( nil )
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should return false and print an error message" do
|
260
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
261
|
+
mock_stderr.should_receive(:puts).with("Expected hash of expected output with (filename, file content) pairs. Unexpected class: #{nil.class}")
|
262
|
+
subject.verify_output(order_matters).should == false
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
context "when there is one or more output files to compare and there are no type errors" do
|
267
|
+
let(:file_paths) { [File.join(generate_unique_str + ".txt"),
|
268
|
+
File.join(generate_unique_str + ".txt")] }
|
269
|
+
let(:file_contents) { ["hello world\ngoodbye world\nhi world\nhey world\nlater world\nbye world\n",
|
270
|
+
"hello universe\ngoodbye universe\nhi universe\nhey universe\nlater universe\nbye universe"] }
|
271
|
+
let(:actual_output) { Hash[*file_paths.zip(file_contents).flatten] }
|
272
|
+
let(:reordered_output) { actual_output.merge( {file_paths[0] => file_contents[0].split("\n").sort.join("\n")} ) }
|
273
|
+
|
274
|
+
before do
|
275
|
+
subject.stub(:input_dir).and_return(generate_unique_str)
|
276
|
+
FileUtils.mkdir_p(subject.input_dir)
|
277
|
+
|
278
|
+
actual_output.keys.each do |file_path|
|
279
|
+
File.open(File.join(subject.input_dir, file_path), "w") { |f| f.print(actual_output[file_path]) }
|
280
|
+
end
|
281
|
+
|
282
|
+
subject.stub(:output_files).and_return( actual_output )
|
283
|
+
|
284
|
+
reordered_output.should_not == actual_output
|
285
|
+
end
|
286
|
+
|
287
|
+
after do
|
288
|
+
FileUtils.rm_rf(subject.input_dir) if File.exists?(subject.input_dir)
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should return false when the Pig script exits with a non-zero exit code" do
|
292
|
+
subject.stub(:exit_code).and_return(123)
|
293
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
294
|
+
mock_stderr.should_receive(:puts).with("Pig script exited with non-zero exit code: 123.")
|
295
|
+
subject.verify_output(order_matters).should == false
|
296
|
+
end
|
297
|
+
|
298
|
+
context "when the actual output has one or more lines than the expected output" do
|
299
|
+
let(:mismatch) { actual_output[file_paths[0]].split("\n")[0..-2].join("\n") }
|
300
|
+
before do
|
301
|
+
subject.stub(:output_files).and_return( actual_output.merge( file_paths[0] => mismatch ) )
|
302
|
+
end
|
303
|
+
it "should print the first extra line and return false" do
|
304
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
305
|
+
mock_stderr.should_receive(:puts).with("Mismatch detected in '#{file_paths[0]}':")
|
306
|
+
mock_stderr.should_receive(:puts).with("\tExpected line: ''")
|
307
|
+
mock_stderr.should_receive(:puts).with("\tActual line: '#{actual_output[file_paths[0]].split("\n")[-1]}'")
|
308
|
+
subject.verify_output(order_matters).should == false
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
context "when the expected output has one or more lines than the actual output" do
|
313
|
+
let(:mismatch) { actual_output[file_paths[0]] + "extra line\n" }
|
314
|
+
before do
|
315
|
+
subject.stub(:output_files).and_return( actual_output.merge( file_paths[0] => mismatch ) )
|
316
|
+
end
|
317
|
+
it "should print the first extra line and return false" do
|
318
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
319
|
+
mock_stderr.should_receive(:puts).with("Mismatch detected in '#{file_paths[0]}':")
|
320
|
+
mock_stderr.should_receive(:puts).with("\tExpected line: '#{mismatch.split("\n")[-1]}'")
|
321
|
+
mock_stderr.should_receive(:puts).with("\tActual line: ''")
|
322
|
+
subject.verify_output(order_matters).should == false
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context "when output order matters" do
|
327
|
+
let(:order_matters) { true }
|
328
|
+
|
329
|
+
context "when the output is exactly the same as the file contents" do
|
330
|
+
it "should return true" do
|
331
|
+
subject.verify_output(order_matters).should == true
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
context "when the output is not exactly the same as the file contents" do
|
336
|
+
before do
|
337
|
+
subject.stub(:output_files).and_return( reordered_output )
|
338
|
+
end
|
339
|
+
|
340
|
+
it "should return false" do
|
341
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
342
|
+
mock_stderr.should_receive(:puts).with("Mismatch detected in '#{file_paths[0]}':")
|
343
|
+
mock_stderr.should_receive(:puts).with("\tExpected line: '#{reordered_output[file_paths[0]].split("\n")[0]}'")
|
344
|
+
mock_stderr.should_receive(:puts).with("\tActual line: '#{actual_output[file_paths[0]].split("\n")[0]}'")
|
345
|
+
subject.verify_output(order_matters).should == false
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
context "when output order doesn't matter" do
|
351
|
+
let(:order_matters) { false }
|
352
|
+
|
353
|
+
context "when expected output contains the same lines as the file, but in a different order" do
|
354
|
+
before do
|
355
|
+
subject.stub(:output_files).and_return( reordered_output )
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should return true" do
|
359
|
+
subject.verify_output(order_matters).should == true
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
context "when expected output does not contain the same lines as the file" do
|
364
|
+
let(:mismatch) { reordered_output[file_paths[0]] + "2" }
|
365
|
+
before do
|
366
|
+
subject.stub(:output_files).and_return( reordered_output.merge( file_paths[0] => mismatch ) )
|
367
|
+
end
|
368
|
+
it "should return false" do
|
369
|
+
subject.should_receive(:error_stream).and_return(mock_stderr)
|
370
|
+
mock_stderr.should_receive(:puts).with("Mismatch detected in '#{file_paths[0]}':")
|
371
|
+
mock_stderr.should_receive(:puts).with("\tExpected line: '#{mismatch.split("\n")[-1]}'")
|
372
|
+
mock_stderr.should_receive(:puts).with("\tActual line: '#{mismatch[0, mismatch.size - 1].split("\n")[-1]}'")
|
373
|
+
subject.verify_output(order_matters).should == false
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pig-spec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Matt Martin
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-11-21 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
description: PigSpec is a PigUnit-like program implemented in Ruby. It can be easily included into RSpec for running integration tests of existing Pig scripts.
|
35
|
+
email:
|
36
|
+
- matt [dot] martin [at] thinkbiganalytics [dot] com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- Gemfile
|
46
|
+
- README.md
|
47
|
+
- Rakefile
|
48
|
+
- lib/pig-spec.rb
|
49
|
+
- lib/pig-spec/pig_unit_core.rb
|
50
|
+
- lib/pig-spec/version.rb
|
51
|
+
- pig-spec.gemspec
|
52
|
+
- spec/pig-spec/pig_unit_core_spec.rb
|
53
|
+
- spec/spec_helper.rb
|
54
|
+
homepage: http://www.thinkbiganalytics.com/
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
hash: 3
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.8.11
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: PigSpec is a PigUnit-like program implemented in Ruby.
|
87
|
+
test_files:
|
88
|
+
- spec/pig-spec/pig_unit_core_spec.rb
|
89
|
+
- spec/spec_helper.rb
|