aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorsucanjan <sucanjan@fit.cvut.cz>2018-02-14 21:57:30 +0100
committersucanjan <sucanjan@fit.cvut.cz>2018-02-14 21:57:30 +0100
commit7b34bab6af30df9cfca7df9e0c644a0ea967ff04 (patch)
tree8eed65392f403669b5d57f36115366266b2c50db /test
Initial commit
Diffstat (limited to 'test')
-rw-r--r--test/datasets/size_10.dataset4
-rw-r--r--test/datasets/size_4.dataset4
-rw-r--r--test/invalid_datasets/invalid_1.dataset0
-rw-r--r--test/invalid_datasets/invalid_2.dataset3
-rw-r--r--test/invalid_datasets/invalid_3.dataset4
-rw-r--r--test/invalid_datasets/invalid_4.dataset4
-rw-r--r--test/invalid_datasets/invalid_5.dataset2
-rw-r--r--test/invalid_datasets/invalid_6.dataset2
-rw-r--r--test/invalid_datasets/invalid_7.dataset4
-rw-r--r--test/invalid_datasets/invalid_8.dataset4
-rw-r--r--test/knapsack_solver_matchers.rb103
-rw-r--r--test/knapsack_solver_spec.rb261
-rw-r--r--test/output_logs/size_10_dynamic_programming.results5
-rw-r--r--test/output_logs/size_10_dynamic_programming.stats3
-rw-r--r--test/output_logs/size_10_fptas.results5
-rw-r--r--test/output_logs/size_10_fptas.stats3
-rw-r--r--test/output_logs/size_10_heuristic.results5
-rw-r--r--test/output_logs/size_10_heuristic.stats3
-rw-r--r--test/output_logs/size_4_dynamic_programming.results5
-rw-r--r--test/output_logs/size_4_dynamic_programming.stats3
-rw-r--r--test/output_logs/size_4_fptas.results5
-rw-r--r--test/output_logs/size_4_fptas.stats3
-rw-r--r--test/output_logs/size_4_heuristic.results5
-rw-r--r--test/output_logs/size_4_heuristic.stats3
-rw-r--r--test/spec_helper.rb5
25 files changed, 448 insertions, 0 deletions
diff --git a/test/datasets/size_10.dataset b/test/datasets/size_10.dataset
new file mode 100644
index 0000000..bcf3857
--- /dev/null
+++ b/test/datasets/size_10.dataset
@@ -0,0 +1,4 @@
+10
+100 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 100 99
+100 5 225 2 248 8 189 7 221 68 228 6 31 1 91 12 140 21 112 22 118
+100 2 125 1 239 35 223 4 145 7 249 19 78 12 174 3 125 34 221 11 93
diff --git a/test/datasets/size_4.dataset b/test/datasets/size_4.dataset
new file mode 100644
index 0000000..0ae0363
--- /dev/null
+++ b/test/datasets/size_4.dataset
@@ -0,0 +1,4 @@
+4
+100 65 38 20 243 13 107 39 122
+100 21 29 17 190 86 88 52 95
+100 64 155 20 62 52 56 2 170
diff --git a/test/invalid_datasets/invalid_1.dataset b/test/invalid_datasets/invalid_1.dataset
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/invalid_datasets/invalid_1.dataset
diff --git a/test/invalid_datasets/invalid_2.dataset b/test/invalid_datasets/invalid_2.dataset
new file mode 100644
index 0000000..7b6743e
--- /dev/null
+++ b/test/invalid_datasets/invalid_2.dataset
@@ -0,0 +1,3 @@
+100 65 38 20 243 13 107 39 122
+100 21 29 17 190 86 88 52 95
+100 64 155 20 62 52 56 2 170
diff --git a/test/invalid_datasets/invalid_3.dataset b/test/invalid_datasets/invalid_3.dataset
new file mode 100644
index 0000000..f56eb9a
--- /dev/null
+++ b/test/invalid_datasets/invalid_3.dataset
@@ -0,0 +1,4 @@
+-4
+100 65 38 20 243 13 107 39 122
+100 21 29 17 190 86 88 52 95
+100 64 155 20 62 52 56 2 170
diff --git a/test/invalid_datasets/invalid_4.dataset b/test/invalid_datasets/invalid_4.dataset
new file mode 100644
index 0000000..f976d55
--- /dev/null
+++ b/test/invalid_datasets/invalid_4.dataset
@@ -0,0 +1,4 @@
+4.2
+100 65 38 20 243 13 107 39 122
+100 21 29 17 190 86 88 52 95
+100 64 155 20 62 52 56 2 170
diff --git a/test/invalid_datasets/invalid_5.dataset b/test/invalid_datasets/invalid_5.dataset
new file mode 100644
index 0000000..733787e
--- /dev/null
+++ b/test/invalid_datasets/invalid_5.dataset
@@ -0,0 +1,2 @@
+4
+
diff --git a/test/invalid_datasets/invalid_6.dataset b/test/invalid_datasets/invalid_6.dataset
new file mode 100644
index 0000000..3d81f69
--- /dev/null
+++ b/test/invalid_datasets/invalid_6.dataset
@@ -0,0 +1,2 @@
+4
+100 65 38 20 243 13 107 39
diff --git a/test/invalid_datasets/invalid_7.dataset b/test/invalid_datasets/invalid_7.dataset
new file mode 100644
index 0000000..19bc569
--- /dev/null
+++ b/test/invalid_datasets/invalid_7.dataset
@@ -0,0 +1,4 @@
+4
+100 65 38 20 243 13 107 39 122
+100 21 29 17 190 86 88 52 95
+100 -64 155 20 62 52 56 2 170
diff --git a/test/invalid_datasets/invalid_8.dataset b/test/invalid_datasets/invalid_8.dataset
new file mode 100644
index 0000000..3a94b69
--- /dev/null
+++ b/test/invalid_datasets/invalid_8.dataset
@@ -0,0 +1,4 @@
+4
+100 65 38 20 243 13 107 39 122
+100 21 29 17 190 86 88.888 52 95
+100 64 155 20 62 52 56 2 170
diff --git a/test/knapsack_solver_matchers.rb b/test/knapsack_solver_matchers.rb
new file mode 100644
index 0000000..754fb20
--- /dev/null
+++ b/test/knapsack_solver_matchers.rb
@@ -0,0 +1,103 @@
+require 'rspec/expectations'
+
+def comment_lines(file_path)
+ File.open(file_path, 'r').each_line.select { |l| l.chars.first == '#' }
+end
+
+def data_lines(file_path)
+ File.open(file_path, 'r').each_line.select { |l| l.chars.first != '#' }
+end
+
+
+RSpec::Matchers.define :be_a_regular_file do
+ match do |actual|
+ File.file?(actual)
+ end
+end
+
+RSpec::Matchers.define :be_an_empty_directory do
+ match do |actual|
+ Dir.empty?(actual)
+ end
+end
+
+RSpec::Matchers.define :be_a_valid_results_file do
+ match do |actual|
+ begin
+ comment_lines = comment_lines(actual)
+ data_lines = data_lines(actual)
+ # It must have 3 lines: 2 comments and >= 1 data line
+ return false if comment_lines.size != 2 || data_lines.size < 1
+ # Data line must consist of non-negative numbers and array 1 and 0
+ data_lines.each do |l|
+ l.scan(/\[[^\]]*\]/).first.tr('[]', '').split(',').map { |n| n.to_i }.each do |i|
+ return false if i != 0 && i != 1
+ end
+ l.scan(/[0-9\.]+/).map { |n| n.to_f }.each do |i|
+ return false if i < 0
+ end
+ end
+ true
+ rescue
+ false
+ end
+ end
+end
+
+RSpec::Matchers.define :be_a_equal_to_results_file do |good|
+ match do |actual|
+ begin
+ lines = data_lines(actual)
+ good_lines = data_lines(good)
+ return false if lines.size != good_lines.size
+ lines.each_with_index do |l, i|
+ # Check price and configuration
+ return false if l.split(']').first != good_lines[i].split(']').first
+ # Check relative error
+ return false if l.split().last != good_lines[i].split().last
+ return false if l.split().size != good_lines[i].split().size
+ end
+ true
+ rescue
+ false
+ end
+ end
+end
+
+RSpec::Matchers.define :be_a_equal_to_stats_file do |good|
+ match do |actual|
+ begin
+ lines = data_lines(actual)
+ good_lines = data_lines(good)
+ return false if lines.size != good_lines.size
+ lines.each_with_index do |l, i|
+ # Check average price
+ return false if l.split().first != good_lines[i].split().first
+ # Check average relative error
+ return false if l.split().last != good_lines[i].split().last
+ return false if l.split().size != good_lines[i].split().size
+ end
+ true
+ rescue
+ false
+ end
+ end
+end
+
+RSpec::Matchers.define :be_a_valid_stats_file do
+ match do |actual|
+ begin
+ comment_lines = comment_lines(actual)
+ data_lines = data_lines(actual)
+ # It must have 3 lines: 2 comments and 1 data line
+ return false if comment_lines.size != 2 || data_lines.size != 1
+ # Data line must consist of non-negative numbers
+ data_lines.first.split.map { |i| i.to_f }.each do |n|
+ return false if n < 0
+ end
+ true
+ rescue
+ false
+ end
+ end
+end
diff --git a/test/knapsack_solver_spec.rb b/test/knapsack_solver_spec.rb
new file mode 100644
index 0000000..9b825a6
--- /dev/null
+++ b/test/knapsack_solver_spec.rb
@@ -0,0 +1,261 @@
+require 'rspec'
+require 'tmpdir'
+require_relative 'spec_helper'
+require_relative 'knapsack_solver_matchers'
+require_relative '../lib/knapsack_solver/cli.rb'
+require_relative '../lib/knapsack_solver/version.rb'
+
+module FileHelper
+ def file_list(directory, files)
+ files.map { |f| File.join(directory, f) }
+ end
+end
+
+module ArgumentHelper
+ def args(args_string = nil)
+ return [] if args_string.nil?
+ args_string.split
+ end
+
+ def args_in_dataset_out_file(args_string, tmpdir)
+ args(args_string) + ['-o', tmpdir, 'test/datasets/size_4.dataset', 'test/datasets/size_10.dataset']
+ end
+end
+
+describe KnapsackSolver::CLI do
+ include FileHelper
+ include ArgumentHelper
+
+ subject(:cli) { KnapsackSolver::CLI }
+
+ context 'options' do
+ it 'recognizes invalid options' do
+ expect { cli.run(args('-a')) }.to raise_error(OptionParser::InvalidOption)
+ expect { cli.run(args('-k -h')) }.to raise_error(OptionParser::InvalidOption)
+ expect { cli.run(args('-x -b')) }.to raise_error(OptionParser::InvalidOption)
+ end
+
+ it 'detects missing arguments' do
+ expect { cli.run(args('-o')) }.to raise_error(OptionParser::MissingArgument)
+ expect { cli.run(args('-g')) }.to raise_error(OptionParser::MissingArgument)
+ end
+
+ it '-h has the top priority among valid options' do
+ expect { cli.run(args('-b -v -h')) }.to output(/Usage:/).to_stdout
+ expect { cli.run(args('-b -h -v')) }.to output(/Usage:/).to_stdout
+ expect { cli.run(args('-h -b -v')) }.to output(/Usage:/).to_stdout
+ end
+
+ it '-v has a second from the top priority among valid options' do
+ expect { cli.run(args('-v -h')) }.to output(/Usage:/).to_stdout
+ expect { cli.run(args('-v -b')) }.to output(/knapsack_solver #{KnapsackSolver::VERSION}/).to_stdout
+ end
+
+ it 'at least one method of solving must be selected' do
+ Dir.mktmpdir() do |tmpdir|
+ expect { cli.run(args()) }.to raise_error(/At least one method of solving must be requested/)
+ expect { cli.run(args_in_dataset_out_file('-b', tmpdir)) }.not_to raise_error
+ end
+ end
+
+ it 'FPTAS must have epsilon constant provided' do
+ Dir.mktmpdir() do |tmpdir|
+ expect { cli.run(args_in_dataset_out_file('-f', tmpdir)) }.to raise_error(/Missing FPTAS epsilon constant/)
+ expect { cli.run(args_in_dataset_out_file('-f -e 0.5', tmpdir)) }.not_to raise_error
+ end
+ end
+
+ it 'FPTAS epsilon constant must be number from range (0,1)' do
+ Dir.mktmpdir() do |tmpdir|
+ expect { cli.run(args_in_dataset_out_file('-f -e asdf', tmpdir)) }.to raise_error(/FPTAS epsilon must be number from range \(0,1\)/)
+ expect { cli.run(args_in_dataset_out_file('-f -e 0.5x', tmpdir)) }.to raise_error(/FPTAS epsilon must be number from range \(0,1\)/)
+ expect { cli.run(args_in_dataset_out_file('-f -e -0.3', tmpdir)) }.to raise_error(/FPTAS epsilon must be number from range \(0,1\)/)
+ expect { cli.run(args_in_dataset_out_file('-f -e 0', tmpdir)) }.to raise_error(/FPTAS epsilon must be number from range \(0,1\)/)
+ expect { cli.run(args_in_dataset_out_file('-f -e 0.01', tmpdir)) }.not_to raise_error
+ expect { cli.run(args_in_dataset_out_file('-f -e 0.99', tmpdir)) }.not_to raise_error
+ expect { cli.run(args_in_dataset_out_file('-f -e 1', tmpdir)) }.to raise_error(/FPTAS epsilon must be number from range \(0,1\)/)
+ end
+ end
+
+ it 'epsilon constant must not be provided when FPTAS is not selected' do
+ Dir.mktmpdir() do |tmpdir|
+ expect { cli.run(args_in_dataset_out_file('-b -e 0.5', tmpdir)) }.to raise_error(/epsilon constant must not be provided when FPTAS is not selected/)
+ expect { cli.run(args_in_dataset_out_file('-b -f -e 0.5', tmpdir)) }.not_to raise_error
+ end
+ end
+
+ end
+
+ context 'positional arguments' do
+ it 'must have at least one dataset provided' do
+ expect { cli.run(args('-b')) }.to raise_error(/Missing datset file\(s\)/)
+ end
+
+ it 'dataset path must be a path to a regular file' do
+ Dir.mktmpdir do |tmpdir|
+ expect { cli.run(args('-b ' + tmpdir)) }.to raise_error(/is not a regular file/)
+ end
+ end
+
+ it 'dataset file must exist' do
+ Dir.mktmpdir do |tmpdir|
+ not_existent_file = File.join(tmpdir, 'size_4.dataset')
+ expect { cli.run(args('-b ' + not_existent_file)) }.to raise_error(/does not exists/)
+ end
+ end
+
+ it 'dataset file must be readable' do
+ Dir.mktmpdir do |tmpdir|
+ FileUtils.cp('test/datasets/size_4.dataset', tmpdir)
+ not_readable_file = File.join(tmpdir, 'size_4.dataset')
+ FileUtils.chmod('a-r', not_readable_file)
+ expect { cli.run(args('-b ' + not_readable_file)) }.to raise_error(/is not readable/)
+ end
+ end
+
+ it 'dataset file must have correct format' do
+ Dir.mktmpdir do |tmpdir|
+ invalid_files = %w(invalid_1.dataset
+ invalid_2.dataset
+ invalid_3.dataset
+ invalid_4.dataset
+ invalid_5.dataset
+ invalid_6.dataset
+ invalid_7.dataset
+ invalid_8.dataset)
+ error_messages = ['missing ID',
+ 'first line does not contain ID',
+ 'ID is negative',
+ 'ID is not an integer',
+ 'missing knapsack capacity',
+ 'missing pairs \(price, weight\)',
+ 'instance desctiption contains negative number',
+ 'instance desctiption does not contain only integers']
+ file_list('test/invalid_datasets', invalid_files).each_with_index do |f, i|
+ expect { cli.run(args('-d ' + f)) }.to raise_error(/#{error_messages[i]}/)
+ end
+ end
+ end
+ end
+
+ context 'output of results' do
+ it 'directory for the output logs must exist' do
+ Dir.mktmpdir do |tmpdir|
+ not_existent_file = File.join(tmpdir, 'size_4.dataset')
+ expect { cli.run(args('-b -o ' + not_existent_file)) }.to raise_error(/does not exists/)
+ end
+ end
+
+ it 'path to a directory for the output logs must point to a directory' do
+ Dir.mktmpdir do |tmpdir|
+ FileUtils.cp('test/datasets/size_4.dataset', tmpdir)
+ file = File.join(tmpdir, 'size_4.dataset')
+ expect { cli.run(args('-b -o ' + file)) }.to raise_error(/is not a directory/)
+ end
+ end
+
+ it 'directory for the output logs must be writable' do
+ Dir.mktmpdir do |tmpdir|
+ not_writable_dir = File.join(tmpdir, 'dir')
+ Dir.mkdir(not_writable_dir)
+ FileUtils.chmod('a-w', not_writable_dir)
+ expect { cli.run(args('-b -o ' + not_writable_dir)) }.to raise_error(/is not writable/)
+ end
+ end
+
+ it 'directory for the graph files must exist' do
+ Dir.mktmpdir do |tmpdir|
+ not_existent_file = File.join(tmpdir, 'size_4.dataset')
+ expect { cli.run(args('-b -g ' + not_existent_file)) }.to raise_error(/does not exists/)
+ end
+ end
+
+ it 'path to a directory for the graph files must point to a directory' do
+ Dir.mktmpdir do |tmpdir|
+ FileUtils.cp('test/datasets/size_4.dataset', tmpdir)
+ file = File.join(tmpdir, 'size_4.dataset')
+ expect { cli.run(args('-b -g ' + file)) }.to raise_error(/is not a directory/)
+ end
+ end
+
+ it 'directory for the graph files must be writable' do
+ Dir.mktmpdir do |tmpdir|
+ not_writable_dir = File.join(tmpdir, 'dir')
+ Dir.mkdir(not_writable_dir)
+ FileUtils.chmod('a-w', not_writable_dir)
+ expect { cli.run(args('-b -g ' + not_writable_dir)) }.to raise_error(/is not writable/)
+ end
+ end
+
+ it 'writes graph files' do
+ Dir.mktmpdir do |tmpdir|
+ png_files = %w(avg_price.png avg_cpu_time.png avg_wall_clock_time.png avg_relative_error.png)
+ gnuplot_files = %w(avg_price.gnuplot avg_cpu_time.gnuplot avg_wall_clock_time.gnuplot avg_relative_error.gnuplot)
+ files = png_files + gnuplot_files
+ cli.run(args_in_dataset_out_file('-b -g ' + tmpdir, tmpdir))
+ expect(file_list(tmpdir, files)).to all be_a_regular_file
+ end
+ end
+
+ it 'writes results and stats to files' do
+ Dir.mktmpdir do |tmpdir|
+ results_files = %w(size_4_branch_and_bound.results size_10_branch_and_bound.results)
+ stats_files = %w(size_4_branch_and_bound.stats size_10_branch_and_bound.stats)
+ files = results_files + stats_files
+ cli.run(args_in_dataset_out_file('-b', tmpdir))
+ expect(file_list(tmpdir, files)).to all be_a_regular_file
+ end
+ end
+
+ it 'writes results and stats to stdout' do
+ results_files = %w(size_4_branch_and_bound.results size_10_branch_and_bound.results)
+ stats_files = %w(size_4_branch_and_bound.stats size_10_branch_and_bound.stats)
+ files = results_files + stats_files
+ files.each do |f|
+ expect { cli.run(args('-b test/datasets/size_4.dataset test/datasets/size_10.dataset')) }.to output(/#{f}/).to_stdout
+ end
+ end
+
+ it 'adds relative error if an exact solving method is selected' do
+ expect { cli.run(args('-r test/datasets/size_4.dataset test/datasets/size_10.dataset')) }.not_to output(/avg_relative_error/).to_stdout
+ expect { cli.run(args('-f -e 0.5 -r test/datasets/size_4.dataset test/datasets/size_10.dataset')) }.not_to output(/avg_relative_error/).to_stdout
+ expect { cli.run(args('-b -r test/datasets/size_4.dataset test/datasets/size_10.dataset')) }.to output(/avg_relative_error/).to_stdout
+ expect { cli.run(args('-d -r test/datasets/size_4.dataset test/datasets/size_10.dataset')) }.to output(/avg_relative_error/).to_stdout
+ end
+
+ it 'produces correct results' do
+ Dir.mktmpdir do |tmpdir|
+ results_files = %w(size_10_dynamic_programming.results
+ size_10_fptas.results
+ size_10_heuristic.results
+ size_4_dynamic_programming.results
+ size_4_fptas.results
+ size_4_heuristic.results)
+ stats_files = %w(size_10_dynamic_programming.stats
+ size_10_fptas.stats
+ size_10_heuristic.stats
+ size_4_dynamic_programming.stats
+ size_4_fptas.stats
+ size_4_heuristic.stats)
+ good_results_files = results_files.map { |f| File.join('test/output_logs', f) }
+ good_stats_files = stats_files.map { |f| File.join('test/output_logs', f) }
+ files = results_files + stats_files
+ cli.run(args_in_dataset_out_file('-b -d -r -f -e 0.5', tmpdir))
+ expect(file_list(tmpdir, files)).to all be_a_regular_file
+ expect(file_list(tmpdir, results_files)).to all be_a_valid_results_file
+ expect(file_list(tmpdir, stats_files)).to all be_a_valid_stats_file
+
+ file_list(tmpdir, results_files).each_with_index do |f, i|
+ expect(f).to be_a_equal_to_results_file(good_results_files[i])
+ end
+
+ file_list(tmpdir, stats_files).each_with_index do |f, i|
+ expect(f).to be_a_equal_to_stats_file(good_stats_files[i])
+ end
+ end
+
+ end
+
+ end
+
+end
diff --git a/test/output_logs/size_10_dynamic_programming.results b/test/output_logs/size_10_dynamic_programming.results
new file mode 100644
index 0000000..62c8189
--- /dev/null
+++ b/test/output_logs/size_10_dynamic_programming.results
@@ -0,0 +1,5 @@
+# ../test/output_logs/size_10_dynamic_programming.results
+# price config cpu_time wall_clock_time relative_error
+100 [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] 0.009999999999999981 0.016795745003037155 0.0
+6 [0, 0, 0, 0, 0, 1, 0, 0, 0, 0] 0.010000000000000009 0.005632286978652701 0.0
+19 [0, 0, 0, 0, 0, 1, 0, 0, 0, 0] 0.010000000000000009 0.011052408022806048 0.0
diff --git a/test/output_logs/size_10_dynamic_programming.stats b/test/output_logs/size_10_dynamic_programming.stats
new file mode 100644
index 0000000..7ec5c52
--- /dev/null
+++ b/test/output_logs/size_10_dynamic_programming.stats
@@ -0,0 +1,3 @@
+# ../test/output_logs/size_10_dynamic_programming.stats
+# avg_price avg_cpu_time avg_wall_clock_time avg_relative_error
+41.666666666666664 0.01 0.011160146668165302 0.0
diff --git a/test/output_logs/size_10_fptas.results b/test/output_logs/size_10_fptas.results
new file mode 100644
index 0000000..154def2
--- /dev/null
+++ b/test/output_logs/size_10_fptas.results
@@ -0,0 +1,5 @@
+# ../test/output_logs/size_10_fptas.results
+# price config cpu_time wall_clock_time relative_error
+100 [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] 0.009999999999999981 0.003663021983811632 0.0
+6 [0, 0, 0, 0, 0, 1, 0, 0, 0, 0] 0.010000000000000009 0.008500695985276252 0.0
+19 [0, 0, 0, 0, 0, 1, 0, 0, 0, 0] 0.010000000000000009 0.013226451963419095 0.0
diff --git a/test/output_logs/size_10_fptas.stats b/test/output_logs/size_10_fptas.stats
new file mode 100644
index 0000000..fdfe485
--- /dev/null
+++ b/test/output_logs/size_10_fptas.stats
@@ -0,0 +1,3 @@
+# ../test/output_logs/size_10_fptas.stats
+# avg_price avg_cpu_time avg_wall_clock_time avg_relative_error
+41.666666666666664 0.01 0.008463389977502326 0.0
diff --git a/test/output_logs/size_10_heuristic.results b/test/output_logs/size_10_heuristic.results
new file mode 100644
index 0000000..7b0058c
--- /dev/null
+++ b/test/output_logs/size_10_heuristic.results
@@ -0,0 +1,5 @@
+# ../test/output_logs/size_10_heuristic.results
+# price config cpu_time wall_clock_time relative_error
+100 [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] 0.009999999999999981 0.010833156033186242 0.0
+0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 0.010000000000000009 0.005375430977437645 1.0
+19 [0, 0, 0, 0, 0, 1, 0, 0, 0, 0] 0.010000000000000009 0.02155825894442387 0.0
diff --git a/test/output_logs/size_10_heuristic.stats b/test/output_logs/size_10_heuristic.stats
new file mode 100644
index 0000000..212314f
--- /dev/null
+++ b/test/output_logs/size_10_heuristic.stats
@@ -0,0 +1,3 @@
+# ../test/output_logs/size_10_heuristic.stats
+# avg_price avg_cpu_time avg_wall_clock_time avg_relative_error
+39.666666666666664 0.01 0.012588948651682585 0.3333333333333333
diff --git a/test/output_logs/size_4_dynamic_programming.results b/test/output_logs/size_4_dynamic_programming.results
new file mode 100644
index 0000000..915cf14
--- /dev/null
+++ b/test/output_logs/size_4_dynamic_programming.results
@@ -0,0 +1,5 @@
+# ../test/output_logs/size_4_dynamic_programming.results
+# price config cpu_time wall_clock_time relative_error
+65 [1, 0, 0, 0] 0.009999999999999995 0.011663347017019987 0.0
+86 [0, 0, 1, 0] 0.010000000000000009 0.006198941962793469 0.0
+52 [0, 0, 1, 0] 0.009999999999999995 0.012558827002067119 0.0
diff --git a/test/output_logs/size_4_dynamic_programming.stats b/test/output_logs/size_4_dynamic_programming.stats
new file mode 100644
index 0000000..fb64352
--- /dev/null
+++ b/test/output_logs/size_4_dynamic_programming.stats
@@ -0,0 +1,3 @@
+# ../test/output_logs/size_4_dynamic_programming.stats
+# avg_price avg_cpu_time avg_wall_clock_time avg_relative_error
+67.66666666666667 0.01 0.010140371993960192 0.0
diff --git a/test/output_logs/size_4_fptas.results b/test/output_logs/size_4_fptas.results
new file mode 100644
index 0000000..e86ea0d
--- /dev/null
+++ b/test/output_logs/size_4_fptas.results
@@ -0,0 +1,5 @@
+# ../test/output_logs/size_4_fptas.results
+# price config cpu_time wall_clock_time relative_error
+65 [1, 0, 0, 0] 0.009999999999999981 0.006782401964301243 0.0
+86 [0, 0, 1, 0] 0.010000000000000009 0.012463745952118188 0.0
+52 [0, 0, 1, 0] 0.010000000000000009 0.006875811057398096 0.0
diff --git a/test/output_logs/size_4_fptas.stats b/test/output_logs/size_4_fptas.stats
new file mode 100644
index 0000000..185ff93
--- /dev/null
+++ b/test/output_logs/size_4_fptas.stats
@@ -0,0 +1,3 @@
+# ../test/output_logs/size_4_fptas.stats
+# avg_price avg_cpu_time avg_wall_clock_time avg_relative_error
+67.66666666666667 0.01 0.008707319657939175 0.0
diff --git a/test/output_logs/size_4_heuristic.results b/test/output_logs/size_4_heuristic.results
new file mode 100644
index 0000000..67825c4
--- /dev/null
+++ b/test/output_logs/size_4_heuristic.results
@@ -0,0 +1,5 @@
+# ../test/output_logs/size_4_heuristic.results
+# price config cpu_time wall_clock_time relative_error
+65 [1, 0, 0, 0] 0.009999999999999995 0.0105733550444711 0.0
+86 [0, 0, 1, 0] 0.010000000000000009 0.00713886899757199 0.0
+52 [0, 0, 1, 0] 0.010000000000000009 0.014065870986087248 0.0
diff --git a/test/output_logs/size_4_heuristic.stats b/test/output_logs/size_4_heuristic.stats
new file mode 100644
index 0000000..889433d
--- /dev/null
+++ b/test/output_logs/size_4_heuristic.stats
@@ -0,0 +1,3 @@
+# ../test/output_logs/size_4_heuristic.stats
+# avg_price avg_cpu_time avg_wall_clock_time avg_relative_error
+67.66666666666667 0.010000000000000004 0.010592698342710113 0.0
diff --git a/test/spec_helper.rb b/test/spec_helper.rb
new file mode 100644
index 0000000..adec803
--- /dev/null
+++ b/test/spec_helper.rb
@@ -0,0 +1,5 @@
+RSpec.configure do |c|
+ c.color = true
+ c.formatter = :documentation
+ c.tty = true
+end