testrus 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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +25 -0
- data/Rakefile +8 -0
- data/bin/testrus +6 -0
- data/lib/testrus.rb +5 -0
- data/lib/testrus/input.rb +36 -0
- data/lib/testrus/input/file.rb +99 -0
- data/lib/testrus/runner.rb +36 -0
- data/lib/testrus/runner/run.rb +71 -0
- data/lib/testrus/tester.rb +69 -0
- data/lib/testrus/tester/formatter/default.rb +57 -0
- data/lib/testrus/version.rb +3 -0
- data/test/fixtures/time_run.txt +21 -0
- data/test/input/file_test.rb +67 -0
- data/test/input_test.rb +39 -0
- data/test/program.rb +1 -0
- data/test/runner/formatter/default.rb +5 -0
- data/test/runner/run_test.rb +62 -0
- data/test/runner_test.rb +17 -0
- data/test/test_helper.rb +20 -0
- data/test/tester_test.rb +37 -0
- data/testrus.gemspec +28 -0
- metadata +192 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Simon H. Eskildsen
|
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,25 @@
|
|
1
|
+
# Testrus
|
2
|
+
|
3
|
+
Aids in testing your programs in informatics competitions.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'testrus'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install testrus
|
18
|
+
|
19
|
+
## Contributing
|
20
|
+
|
21
|
+
1. Fork it
|
22
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
23
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
24
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
25
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/testrus
ADDED
data/lib/testrus.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Testrus
|
2
|
+
class Input
|
3
|
+
attr_accessor :options
|
4
|
+
|
5
|
+
# Public: Initializes a new Input. An Input is the standard object from
|
6
|
+
# an Input source to the Tester.
|
7
|
+
#
|
8
|
+
# options - The Hash specifying the input options:
|
9
|
+
# :input - The String input matching the expected output.
|
10
|
+
# :output - The String expected output from the program for the
|
11
|
+
# associated input.
|
12
|
+
# :name - The String name of the input, this is usually the
|
13
|
+
# number of the test relative to the others, and should be
|
14
|
+
# provided by the source.
|
15
|
+
#
|
16
|
+
def initialize(options)
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Returns the name of the input/output pair.
|
21
|
+
def name
|
22
|
+
@options[:name].strip
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Returns the formatted input.
|
26
|
+
def input
|
27
|
+
@options[:input].strip
|
28
|
+
end
|
29
|
+
|
30
|
+
# Public: Returns the formatted expected output.
|
31
|
+
def output
|
32
|
+
@options[:output].strip
|
33
|
+
end
|
34
|
+
alias_method :expected_output, :output
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Testrus
|
2
|
+
class Input
|
3
|
+
class File
|
4
|
+
# Public: Create a new Input::File object to handle file sources for input
|
5
|
+
# and output.
|
6
|
+
#
|
7
|
+
# context - The Hash specifying context:
|
8
|
+
# :pwd - Working directory for the file source.
|
9
|
+
#
|
10
|
+
def initialize(context = {})
|
11
|
+
@context = context
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: The working directory of the file input source.
|
15
|
+
#
|
16
|
+
# Returns the String of the working directory.
|
17
|
+
def pwd
|
18
|
+
@pwd ||= @context[:pwd].nil? || @context[:pwd].empty? ? default_pwd : @context[:pwd]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Associated the file input and output into Input objects, also
|
22
|
+
# caches it so we do not repeat this relatively costly operation on the
|
23
|
+
# file system.
|
24
|
+
#
|
25
|
+
# Returns an Array of Input objects.
|
26
|
+
def input
|
27
|
+
@input ||= input_files.map do |input|
|
28
|
+
Input.new input: ::File.read(input),
|
29
|
+
output: ::File.read(output_from_input(input)),
|
30
|
+
name: name_from_file_name(input)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :tests, :input
|
34
|
+
|
35
|
+
private
|
36
|
+
# Internal: Finds the input files on the filesystem and expands the paths.
|
37
|
+
#
|
38
|
+
# Returns an Array of Strings of paths to the input files.
|
39
|
+
def input_files
|
40
|
+
@input_files ||= files("input.*")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Internal: Finds the output files on the filesystem and expands the paths.
|
44
|
+
#
|
45
|
+
# Returns an Array of Strings of paths to the output files.
|
46
|
+
def output_files
|
47
|
+
@output_files ||= files("output.*")
|
48
|
+
end
|
49
|
+
|
50
|
+
# Internal: Finds the files with the given wildcard and expands the paths.
|
51
|
+
#
|
52
|
+
# Returns an Array of Strings of expanded paths.
|
53
|
+
def files(wildcard)
|
54
|
+
Dir["#{pwd}/#{wildcard}"].map { |file| ::File.expand_path(file) }
|
55
|
+
end
|
56
|
+
|
57
|
+
# Internal: Finds the input file's counterpart in the Array of
|
58
|
+
# output_files by querying on the suffix number.
|
59
|
+
#
|
60
|
+
# Examples
|
61
|
+
#
|
62
|
+
# output_from_input("/home/testrus/input.2")
|
63
|
+
# #=> "/home/testrus/output.2"
|
64
|
+
#
|
65
|
+
# Returns the full path to the associated output file.
|
66
|
+
def output_from_input(input)
|
67
|
+
output_files.find do |output|
|
68
|
+
name_from_file_name(input) == name_from_file_name(output)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Internal: The name of an input or output file is defined as the last
|
73
|
+
# number in the path name. This method returns an input or output file's
|
74
|
+
# name.
|
75
|
+
#
|
76
|
+
# path - The String path of the input or output file.
|
77
|
+
#
|
78
|
+
# Example
|
79
|
+
#
|
80
|
+
# name_from_file_name("/home/testrus/input.2")
|
81
|
+
# #=> "2"
|
82
|
+
#
|
83
|
+
# name_from_file_name("/home/testrus/input.ohai.2")
|
84
|
+
# #=> "2"
|
85
|
+
#
|
86
|
+
# Returns a String name of the input or output file.
|
87
|
+
def name_from_file_name(path)
|
88
|
+
path.match(/\d+$/)[0]
|
89
|
+
end
|
90
|
+
|
91
|
+
# Internal: Default working directory if none was passed.
|
92
|
+
#
|
93
|
+
# Returns the String path of the working directory.
|
94
|
+
def default_pwd
|
95
|
+
`pwd`.strip
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Testrus
|
2
|
+
class Runner
|
3
|
+
attr_reader :command
|
4
|
+
|
5
|
+
# Internal: Specifies the log file.
|
6
|
+
LOG_FILE = "/tmp/testrus"
|
7
|
+
|
8
|
+
# Responsible for running the actual test against a given command (program)
|
9
|
+
# a run object about the execution of the program against the given input.
|
10
|
+
#
|
11
|
+
# command - The String command to pass the input to
|
12
|
+
#
|
13
|
+
def initialize(command)
|
14
|
+
@command = command
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: Returns an object with the results of running the program against
|
18
|
+
# the input.
|
19
|
+
#
|
20
|
+
# input - The String of input to pass to the program
|
21
|
+
#
|
22
|
+
# Returns a Runner::Run object with information about the run.
|
23
|
+
def run(test)
|
24
|
+
system "echo '#{test.input}' | /usr/bin/time -l #{full_command} &> #{LOG_FILE}"
|
25
|
+
Run.new File.read(LOG_FILE), test
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
# Internal: Returns the full command, where the script is prefixed with its
|
30
|
+
# full path. All options are passed along.
|
31
|
+
def full_command
|
32
|
+
parts = command.split(" ")
|
33
|
+
"#{parts[0..-2].join(" ")} #{`pwd`.strip}/#{parts.last}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Testrus
|
2
|
+
class Runner
|
3
|
+
class Run
|
4
|
+
attr_reader :output, :test
|
5
|
+
|
6
|
+
# Responsible for extracting data from the run of the program. See the
|
7
|
+
# public methods for what information can be extracted. This class only
|
8
|
+
# reads output that is run with the command: `/usr/bin/time -l
|
9
|
+
# #{program}`.
|
10
|
+
#
|
11
|
+
# TODO: Test if this runner works on all UNIX-based operating systems, or
|
12
|
+
# only OS X.
|
13
|
+
#
|
14
|
+
# output - The String output from the command `/usr/bin/time -l program`
|
15
|
+
# test - The Test that the input origins from.
|
16
|
+
def initialize(output, test)
|
17
|
+
@output = output
|
18
|
+
@test = test
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Extracts the memory usage from the output.
|
22
|
+
def memory_usage
|
23
|
+
bytes_to_mb(raw_memory_usage)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Extracts the real time from the output.
|
27
|
+
def real_time
|
28
|
+
time "real"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Extracts the user time from the output.
|
32
|
+
def user_time
|
33
|
+
time "user"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Public: Extracts the sys time from the output.
|
37
|
+
def sys_time
|
38
|
+
time "sys"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Public: Boolean value of whether the test passed.
|
42
|
+
def passed?
|
43
|
+
output == @test.expected_output
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: Within usual constraints for time and memory
|
47
|
+
def within_constraints?
|
48
|
+
memory_usage <= 64.00 && real_time <= 1.00
|
49
|
+
end
|
50
|
+
|
51
|
+
# Public: Extracts the output against the input from the entire output
|
52
|
+
# which also inclues the time and memory information.
|
53
|
+
def output
|
54
|
+
@output.split("\n")[0..-16].join("\n").strip
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def raw_memory_usage
|
59
|
+
@output.match(/(\d+)\s+maximum/)[1].to_f
|
60
|
+
end
|
61
|
+
|
62
|
+
def bytes_to_mb(bytes)
|
63
|
+
bytes / (2 << 20)
|
64
|
+
end
|
65
|
+
|
66
|
+
def time(type)
|
67
|
+
@output.match(/(\d+\.\d+)\s+#{type}/)[1].to_f
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Testrus
|
2
|
+
class Tester
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
# Internal: Defines the relationship between input sources and their
|
6
|
+
# corresponding class names.
|
7
|
+
SOURCES = {
|
8
|
+
:file => Testrus::Input::File
|
9
|
+
}
|
10
|
+
|
11
|
+
# Internal: Defines the formatters to allow specifying a custom formatter in
|
12
|
+
# the options.
|
13
|
+
FORMATTERS = {
|
14
|
+
:default => Formatter::Default
|
15
|
+
}
|
16
|
+
|
17
|
+
# Public: Initializes a tester object. The tester is responsible for
|
18
|
+
# coordinating the input from the input source and the runner, and passing
|
19
|
+
# the final results to the formatter.
|
20
|
+
#
|
21
|
+
# options - The Hash containing the options for the Tester:
|
22
|
+
# :source - The Symbol marking the input source, valid options
|
23
|
+
# are defined in the class constant Tester::SOURCES.
|
24
|
+
# :command - The String specifying the command to run program that
|
25
|
+
# the the input will be given to via STDIN.
|
26
|
+
#
|
27
|
+
def initialize(options)
|
28
|
+
@options = default_options.merge(options)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Maps the human source to the class that is responsible for the
|
32
|
+
# given source.
|
33
|
+
#
|
34
|
+
# Returns a corresponding Class constant.
|
35
|
+
def source
|
36
|
+
@source ||= SOURCES[@options[:source]].new
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Returns the instance of the runner which is responsible for
|
40
|
+
# running the program against the input.
|
41
|
+
def runner
|
42
|
+
@runner ||= Testrus::Runner.new(options[:command])
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Returns the formatter class.
|
46
|
+
def formatter
|
47
|
+
FORMATTERS[@options[:formatter]]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: Runs the input from the source against the runner. Return the
|
51
|
+
# results to the formatter.
|
52
|
+
def run
|
53
|
+
source.tests.each do |test|
|
54
|
+
formatter.new(runner.run(test)).report
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Internal: Defines the default options that are overriden by the options
|
61
|
+
# passed to initialize.
|
62
|
+
def default_options
|
63
|
+
{
|
64
|
+
:formatter => :default,
|
65
|
+
:source => :file
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'colored'
|
2
|
+
|
3
|
+
module Testrus
|
4
|
+
class Tester
|
5
|
+
module Formatter
|
6
|
+
class Default
|
7
|
+
attr_reader :run
|
8
|
+
|
9
|
+
# Public: The formatter is responsible for reporting the run data to the
|
10
|
+
# user. New formatters can easily be added here. The layout of this
|
11
|
+
# formatter should be used for guidance on how to create new formatters.
|
12
|
+
# Also see the documentation for Run and the sources to find what
|
13
|
+
# information is available to the formatter.
|
14
|
+
#
|
15
|
+
# The formatter does not return anything useful, it is simply used to
|
16
|
+
# report to the user. The default formatter simply reports to STDOUT.
|
17
|
+
#
|
18
|
+
#
|
19
|
+
# run - The Run containing the run data (memory usage, output, cpu time,
|
20
|
+
# etc.)
|
21
|
+
def initialize(run)
|
22
|
+
@run = run
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Creates the actual report.
|
26
|
+
def report
|
27
|
+
run.passed? && run.within_constraints? ? report_success : report_failure
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
# Internal: Prints the header in the appropriate color.
|
32
|
+
def header(color)
|
33
|
+
puts "---> Test #{run.test.name} (#{run.real_time}s, #{run.memory_usage})".send(color)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Internal: Test passed.
|
37
|
+
def report_success
|
38
|
+
header :green
|
39
|
+
end
|
40
|
+
|
41
|
+
# Internal: Test failed.
|
42
|
+
def report_failure
|
43
|
+
header :red
|
44
|
+
|
45
|
+
puts "Input".yellow
|
46
|
+
puts "#{run.test.input}\n\n"
|
47
|
+
|
48
|
+
puts "Expected".yellow
|
49
|
+
puts "#{run.test.output}\n\n"
|
50
|
+
|
51
|
+
puts "Output".yellow
|
52
|
+
puts "#{run.output}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
hello world
|
2
|
+
you are fine
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
0.18 real 0.02 user 0.11 sys
|
8
|
+
552960 maximum resident set size
|
9
|
+
0 average shared memory size
|
10
|
+
0 average unshared data size
|
11
|
+
0 average unshared stack size
|
12
|
+
159 page reclaims
|
13
|
+
0 page faults
|
14
|
+
0 swaps
|
15
|
+
0 block input operations
|
16
|
+
0 block output operations
|
17
|
+
0 messages sent
|
18
|
+
0 messages received
|
19
|
+
0 signals received
|
20
|
+
0 voluntary context switches
|
21
|
+
1 involuntary context switches
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'testrus/input/file'
|
3
|
+
require 'testrus/input'
|
4
|
+
|
5
|
+
class InputFileTest < Test::Unit::TestCase
|
6
|
+
def root_path
|
7
|
+
File.expand_path(File.dirname(__FILE__) + "/../..")
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@input_file = Testrus::Input::File.new(pwd: root_path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_empty_pwd_context_defaults_to_pwd
|
15
|
+
@input_file = Testrus::Input::File.new(pwd: "")
|
16
|
+
mock(@input_file).default_pwd { '/home/testrus/project' }
|
17
|
+
|
18
|
+
assert_equal "/home/testrus/project", @input_file.pwd
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_nil_pwd_context_defaults_to_pwd
|
22
|
+
@input_file = Testrus::Input::File.new(pwd: nil)
|
23
|
+
mock(@input_file).default_pwd { '/home/testrus/project' }
|
24
|
+
|
25
|
+
assert_equal "/home/testrus/project", @input_file.pwd
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_fetch_and_associate_simple_file_tests
|
29
|
+
File.open("#{root_path}/input.1", "w") { |f| f.write "3 1 2 3" }
|
30
|
+
File.open("#{root_path}/output.1", "w") { |f| f.write "6" }
|
31
|
+
|
32
|
+
parsed_input = @input_file.input
|
33
|
+
|
34
|
+
assert_equal "3 1 2 3", parsed_input.first.input
|
35
|
+
assert_equal "6", parsed_input.first.output
|
36
|
+
assert_equal "1", parsed_input.first.name
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_fetch_and_associate_in_out_format_file_tests
|
40
|
+
File.open("#{root_path}/input.in.1", "w") { |f| f.write "3 1 2 3" }
|
41
|
+
File.open("#{root_path}/output.in.1", "w") { |f| f.write "6" }
|
42
|
+
|
43
|
+
parsed_input = @input_file.input
|
44
|
+
|
45
|
+
assert_equal "3 1 2 3", parsed_input.first.input
|
46
|
+
assert_equal "6", parsed_input.first.output
|
47
|
+
assert_equal "1", parsed_input.first.name
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_fetch_and_ssociate_multiple_simple_file_tests
|
51
|
+
File.open("#{root_path}/input.1", "w") { |f| f.write "3 1 2 3" }
|
52
|
+
File.open("#{root_path}/output.1", "w") { |f| f.write "6" }
|
53
|
+
|
54
|
+
File.open("#{root_path}/input.2", "w") { |f| f.write "2 1 2" }
|
55
|
+
File.open("#{root_path}/output.2", "w") { |f| f.write "3" }
|
56
|
+
|
57
|
+
parsed_input = @input_file.input
|
58
|
+
|
59
|
+
assert_equal "3 1 2 3", parsed_input.first.input
|
60
|
+
assert_equal "6", parsed_input.first.output
|
61
|
+
assert_equal "1", parsed_input.first.name
|
62
|
+
|
63
|
+
assert_equal "2 1 2", parsed_input[1].input
|
64
|
+
assert_equal "3", parsed_input[1].output
|
65
|
+
assert_equal "2", parsed_input[1].name
|
66
|
+
end
|
67
|
+
end
|
data/test/input_test.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'testrus/input'
|
3
|
+
|
4
|
+
class TestInput < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@input = Testrus::Input.new(input: "3 1 2 3", output: "6", name: "1")
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_input
|
10
|
+
assert_equal "3 1 2 3", @input.input
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_output
|
14
|
+
assert_equal "6", @input.output
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_options
|
18
|
+
assert_equal({input: "3 1 2 3", output: "6", name: "1"}, @input.options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_stripped_input
|
22
|
+
@input.options[:input] = "\n\n3 1 2 3\n\r\n"
|
23
|
+
assert_equal "3 1 2 3", @input.input
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_stripped_output
|
27
|
+
@input.options[:output] = "\n\n6\n\r\n"
|
28
|
+
assert_equal "6", @input.output
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_name
|
32
|
+
assert_equal "1", @input.name
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_stripped_name
|
36
|
+
@input.options[:name] = "\n\r1\r\n"
|
37
|
+
assert_equal "1", @input.name
|
38
|
+
end
|
39
|
+
end
|
data/test/program.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts $stdin.gets
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RunnerRunTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@output = fixture("time_run.txt")
|
6
|
+
@test = Testrus::Input.new(input: "world", output: "hello world\nyou are fine", name: "1")
|
7
|
+
|
8
|
+
@run = Testrus::Runner::Run.new(@output, @test)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_extract_memory_usage
|
12
|
+
assert_equal 0.263671875, @run.memory_usage, 0.01
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_extract_real_time
|
16
|
+
assert_equal 0.18, @run.real_time
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_extract_user_time
|
20
|
+
assert_equal 0.02, @run.user_time
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_extract_sys_time
|
24
|
+
assert_equal 0.11, @run.sys_time
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_extract_output
|
28
|
+
assert_equal "hello world\nyou are fine", @run.output
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_passed
|
32
|
+
assert @run.passed?, "Run should pass"
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_not_passed
|
36
|
+
@test = Testrus::Input.new(input: "world", output: "hello world\nyou are not fine", name: "1")
|
37
|
+
@run = Testrus::Runner::Run.new(@output, @test)
|
38
|
+
|
39
|
+
assert !@run.passed?, "Run should not pass"
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_within_constraints
|
43
|
+
stub(@run).real_time { 0.84 }
|
44
|
+
stub(@run).memory_usage { 37.00 }
|
45
|
+
|
46
|
+
assert @run.within_constraints?
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_not_within_time_constraints
|
50
|
+
stub(@run).real_time { 1.84 }
|
51
|
+
stub(@run).memory_usage { 37.00 }
|
52
|
+
|
53
|
+
assert !@run.within_constraints?
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_not_within_memory_constraints
|
57
|
+
stub(@run).real_time { 0.84 }
|
58
|
+
stub(@run).memory_usage { 84.00 }
|
59
|
+
|
60
|
+
assert !@run.within_constraints?
|
61
|
+
end
|
62
|
+
end
|
data/test/runner_test.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RunnerTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@path = File.expand_path(File.join(File.dirname(__FILE__), "/program.rb"))
|
6
|
+
@runner = Testrus::Runner.new("ruby #{@path}")
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_receives_readable_command
|
10
|
+
assert_equal "ruby #{@path}", @runner.command
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_runs_test_with_given_input
|
14
|
+
input = Testrus::Input.new input: "6", output: "6", name: "1"
|
15
|
+
assert_instance_of Testrus::Runner::Run, @runner.run(input)
|
16
|
+
end
|
17
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test-unit'
|
3
|
+
require 'rr'
|
4
|
+
require 'pry'
|
5
|
+
|
6
|
+
$: << File.expand_path(File.dirname(__FILE__) + "../lib")
|
7
|
+
|
8
|
+
require 'testrus'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
include RR::Adapters::TestUnit
|
12
|
+
|
13
|
+
def test_path
|
14
|
+
File.expand_path(File.dirname(__FILE__))
|
15
|
+
end
|
16
|
+
|
17
|
+
def fixture(name)
|
18
|
+
File.read "#{test_path}/fixtures/#{name}"
|
19
|
+
end
|
20
|
+
end
|
data/test/tester_test.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'testrus/input'
|
3
|
+
require 'testrus/input/file'
|
4
|
+
require 'testrus/tester'
|
5
|
+
|
6
|
+
class TesterTest < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@tester = Testrus::Tester.new(:source => :file, :command => "ruby test.rb")
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_set_correct_input_source
|
12
|
+
assert_instance_of Testrus::Input::File, @tester.source
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_set_correct_runner
|
16
|
+
assert_instance_of Testrus::Runner, @tester.runner
|
17
|
+
assert_equal "ruby test.rb", @tester.runner.command
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_default_formatter
|
21
|
+
assert_equal :default, @tester.options[:formatter]
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_default_source
|
25
|
+
assert_equal :file, @tester.options[:source]
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_return_formatter_class
|
29
|
+
assert_equal Testrus::Tester::Formatter::Default, @tester.formatter
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_override_default_options
|
33
|
+
@tester = Testrus::Tester.new(:source => :doom)
|
34
|
+
|
35
|
+
assert_equal :doom, @tester.options[:source]
|
36
|
+
end
|
37
|
+
end
|
data/testrus.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'testrus/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "testrus"
|
8
|
+
gem.version = Testrus::VERSION
|
9
|
+
gem.authors = ["Simon H. Eskildsen"]
|
10
|
+
gem.email = ["sirup@sirupsen.com"]
|
11
|
+
gem.description = %q{Friendly walrus aiding participators in informatics competitions to test their programs.}
|
12
|
+
gem.summary = %q{Testing for informatics competitions.}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.add_dependency("colored", "1.2")
|
16
|
+
|
17
|
+
gem.add_development_dependency("test-unit", "2.5.2")
|
18
|
+
gem.add_development_dependency("fakefs", "0.4.1")
|
19
|
+
gem.add_development_dependency("pry", "0.9.10")
|
20
|
+
gem.add_development_dependency("rr", "1.0.4")
|
21
|
+
gem.add_development_dependency("test-unit", "2.5.3")
|
22
|
+
gem.add_development_dependency("rake", "10.0.2")
|
23
|
+
|
24
|
+
gem.files = `git ls-files`.split($/)
|
25
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
26
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
27
|
+
gem.require_paths = ["lib"]
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: testrus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Simon H. Eskildsen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: colored
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - '='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - '='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.2'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: test-unit
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - '='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.5.2
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.5.2
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: fakefs
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - '='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.4.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.4.1
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pry
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - '='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.9.10
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - '='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.9.10
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rr
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - '='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.0.4
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - '='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.0.4
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: test-unit
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - '='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.5.3
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - '='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.5.3
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rake
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - '='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 10.0.2
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - '='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 10.0.2
|
126
|
+
description: Friendly walrus aiding participators in informatics competitions to test
|
127
|
+
their programs.
|
128
|
+
email:
|
129
|
+
- sirup@sirupsen.com
|
130
|
+
executables:
|
131
|
+
- testrus
|
132
|
+
extensions: []
|
133
|
+
extra_rdoc_files: []
|
134
|
+
files:
|
135
|
+
- .gitignore
|
136
|
+
- Gemfile
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- bin/testrus
|
141
|
+
- lib/testrus.rb
|
142
|
+
- lib/testrus/input.rb
|
143
|
+
- lib/testrus/input/file.rb
|
144
|
+
- lib/testrus/runner.rb
|
145
|
+
- lib/testrus/runner/run.rb
|
146
|
+
- lib/testrus/tester.rb
|
147
|
+
- lib/testrus/tester/formatter/default.rb
|
148
|
+
- lib/testrus/version.rb
|
149
|
+
- test/fixtures/time_run.txt
|
150
|
+
- test/input/file_test.rb
|
151
|
+
- test/input_test.rb
|
152
|
+
- test/program.rb
|
153
|
+
- test/runner/formatter/default.rb
|
154
|
+
- test/runner/run_test.rb
|
155
|
+
- test/runner_test.rb
|
156
|
+
- test/test_helper.rb
|
157
|
+
- test/tester_test.rb
|
158
|
+
- testrus.gemspec
|
159
|
+
homepage: ''
|
160
|
+
licenses: []
|
161
|
+
post_install_message:
|
162
|
+
rdoc_options: []
|
163
|
+
require_paths:
|
164
|
+
- lib
|
165
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
166
|
+
none: false
|
167
|
+
requirements:
|
168
|
+
- - ! '>='
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
none: false
|
173
|
+
requirements:
|
174
|
+
- - ! '>='
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: '0'
|
177
|
+
requirements: []
|
178
|
+
rubyforge_project:
|
179
|
+
rubygems_version: 1.8.23
|
180
|
+
signing_key:
|
181
|
+
specification_version: 3
|
182
|
+
summary: Testing for informatics competitions.
|
183
|
+
test_files:
|
184
|
+
- test/fixtures/time_run.txt
|
185
|
+
- test/input/file_test.rb
|
186
|
+
- test/input_test.rb
|
187
|
+
- test/program.rb
|
188
|
+
- test/runner/formatter/default.rb
|
189
|
+
- test/runner/run_test.rb
|
190
|
+
- test/runner_test.rb
|
191
|
+
- test/test_helper.rb
|
192
|
+
- test/tester_test.rb
|