testrus 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|