codersdojo 0.9.7 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- data/app/argument_parser.rb +36 -0
- data/app/codersdojo.rb +4 -618
- data/app/console_view.rb +108 -0
- data/app/controller.rb +50 -0
- data/app/filename_formatter.rb +34 -0
- data/app/progress.rb +19 -0
- data/app/runner.rb +39 -0
- data/app/scheduler.rb +15 -0
- data/app/session_id_generator.rb +17 -0
- data/app/shell_wrapper.rb +96 -0
- data/app/state_reader.rb +57 -0
- data/app/uploader.rb +49 -0
- data/app/xml_element_extractor.rb +11 -0
- data/bin/codersdojo +4 -0
- data/spec/argument_parser_spec.rb +54 -0
- data/spec/progress_spec.rb +23 -0
- data/spec/runner_spec.rb +59 -0
- data/spec/session_id_generator_spec.rb +21 -0
- data/spec/state_reader_spec.rb +27 -0
- data/spec/uploader_spec.rb +78 -0
- data/spec/xml_element_extractor_spec.rb +12 -0
- data/templates/any/README +10 -0
- data/templates/any/run-endless.sh +2 -0
- data/templates/any/run-once.sh +1 -0
- data/templates/clojure.is-test/README +12 -0
- data/templates/clojure.is-test/lib/clojure-contrib.jar +0 -0
- data/templates/clojure.is-test/run-endless.sh +1 -0
- data/templates/clojure.is-test/run-once.sh +2 -0
- data/templates/java.junit/README +20 -0
- data/templates/java.junit/run-endless.sh +1 -0
- data/templates/java.junit/run-once.sh +3 -0
- data/templates/javascript.jspec/README +8 -0
- data/templates/python.unittest/README +8 -0
- data/templates/ruby.test-unit/README +5 -0
- metadata +42 -5
- data/spec/codersdojo_spec.rb +0 -265
data/app/console_view.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'shell_wrapper'
|
2
|
+
|
3
|
+
class ConsoleView
|
4
|
+
|
5
|
+
@@VERSION = '0.9'
|
6
|
+
|
7
|
+
def show_help
|
8
|
+
puts <<-helptext
|
9
|
+
Personal CodersDojo, Version #{@@VERSION}, http://www.codersdojo.com, Copyright by it-agile GmbH (http://www.it-agile.de)
|
10
|
+
PersonalCodersDojo automatically runs your tests of a code kata.
|
11
|
+
|
12
|
+
helptext
|
13
|
+
show_usage
|
14
|
+
end
|
15
|
+
|
16
|
+
def show_usage
|
17
|
+
puts <<-helptext
|
18
|
+
Usage: #{$0} command [options]
|
19
|
+
Commands:
|
20
|
+
help, -h, --help Print this help text.
|
21
|
+
help <command> See the details of the command.
|
22
|
+
setup <framework> <kata_file> Setup the environment for running the kata.
|
23
|
+
upload <framework> <session_dir> Upload the kata to http://www.codersdojo.org
|
24
|
+
|
25
|
+
Report bugs to <codersdojo@it-agile.de>
|
26
|
+
helptext
|
27
|
+
end
|
28
|
+
|
29
|
+
def show_detailed_help command
|
30
|
+
if command == 'setup' then
|
31
|
+
show_help_setup
|
32
|
+
elsif command == 'start' then
|
33
|
+
show_help_start
|
34
|
+
elsif command == 'upload' then
|
35
|
+
show_help_upload
|
36
|
+
else
|
37
|
+
show_help_unknown command
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def show_help_setup
|
42
|
+
templates = ShellWrapper.new.list_templates
|
43
|
+
puts <<-helptext
|
44
|
+
|
45
|
+
setup <framework> <kata_file_no_ext> Setup the environment for the kata for the given framework and kata file.
|
46
|
+
The kata_file should not have an extension. Use 'prime' and not 'prime.java'.
|
47
|
+
By now <framework> is one of #{templates}.
|
48
|
+
Use ??? as framework if your framework isn't in the list.
|
49
|
+
|
50
|
+
Example:
|
51
|
+
:/dojo/my_kata% #{$0} setup ruby.test/unit prime
|
52
|
+
Show the instructions how to setup the environment for kata execution with Ruby and test/unit.
|
53
|
+
helptext
|
54
|
+
end
|
55
|
+
|
56
|
+
def show_help_start
|
57
|
+
puts <<-helptext
|
58
|
+
|
59
|
+
start <shell_command> <kata_file> Start the continuous test runner, that runs <shell-command> whenever <kata_file>
|
60
|
+
changes. The <kata_file> has to include the whole source code of the kata.
|
61
|
+
Whenever the test runner is started, it creates a new session directory in the
|
62
|
+
directory .codersdojo where it logs the steps of the kata.
|
63
|
+
helptext
|
64
|
+
end
|
65
|
+
|
66
|
+
def show_help_upload
|
67
|
+
templates = ShellWrapper.new.list_templates
|
68
|
+
puts <<-helptext
|
69
|
+
|
70
|
+
upload <framework> <session_directory> Upload the kata written with <framework> in <session_directory> to codersdojo.com.
|
71
|
+
<session_directory> is relative to the working directory.
|
72
|
+
By now <framework> is one of #{templates}.
|
73
|
+
If you used another framework, use ??? and send an email to codersdojo@it-agile.de
|
74
|
+
|
75
|
+
Example:
|
76
|
+
:/dojo/my_kata$ #{$0} upload ruby.test/unit .codersdojo/2010-11-02_16-21-53
|
77
|
+
Upload the kata (written in Ruby with the test/unit framework) located in directory ".codersdojo/2010-11-02_16-21-53" to codersdojo.com.
|
78
|
+
helptext
|
79
|
+
end
|
80
|
+
|
81
|
+
def show_help_unknown command
|
82
|
+
puts <<-helptext
|
83
|
+
Command #{command} not known. Try '#{$0} help' to list the supported commands.
|
84
|
+
helptext
|
85
|
+
end
|
86
|
+
|
87
|
+
def show_start_kata command, file
|
88
|
+
puts "Starting PersonalCodersDojo with command #{command} and kata file #{file}. Use Ctrl+C to finish the kata."
|
89
|
+
end
|
90
|
+
|
91
|
+
def show_missing_command_argument_error command
|
92
|
+
puts "Command <#{command}> recognized but no argument was provided (at least one argument is required).\n\n"
|
93
|
+
show_usage
|
94
|
+
end
|
95
|
+
|
96
|
+
def show_upload_start hostname
|
97
|
+
puts "Start upload to #{hostname}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def show_upload_result result
|
101
|
+
puts result
|
102
|
+
end
|
103
|
+
|
104
|
+
def show_socket_error command
|
105
|
+
puts "Encountered network error while <#{command}>. Is http://www.codersdojo.com down?"
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
data/app/controller.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
class Controller
|
2
|
+
|
3
|
+
def initialize view, hostname
|
4
|
+
@view = view
|
5
|
+
@hostname = hostname
|
6
|
+
end
|
7
|
+
|
8
|
+
def help command=nil
|
9
|
+
if command then
|
10
|
+
@view.show_detailed_help command.downcase
|
11
|
+
else
|
12
|
+
@view.show_help
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate framework, kata_file
|
17
|
+
shell = ShellWrapper.new
|
18
|
+
shell.scaffold framework
|
19
|
+
generator_text = IO.readlines("README").to_s;
|
20
|
+
generator_text = generator_text.gsub('#{kata_file}', kata_file)
|
21
|
+
generator_text = generator_text.gsub('#{$0}', $0)
|
22
|
+
generator_text = shell.make_os_specific generator_text
|
23
|
+
puts "\n" + generator_text
|
24
|
+
end
|
25
|
+
|
26
|
+
def start command, file
|
27
|
+
if not command or not file then
|
28
|
+
@view.show_missing_command_argument_error "start"
|
29
|
+
return
|
30
|
+
end
|
31
|
+
@view.show_start_kata command, file
|
32
|
+
dojo = Runner.new ShellWrapper.new, SessionIdGenerator.new
|
33
|
+
dojo.file = file
|
34
|
+
dojo.run_command = command
|
35
|
+
scheduler = Scheduler.new dojo
|
36
|
+
scheduler.start
|
37
|
+
end
|
38
|
+
|
39
|
+
def upload framework, session_directory
|
40
|
+
@view.show_upload_start @hostname
|
41
|
+
if session_directory then
|
42
|
+
uploader = Uploader.new @hostname, framework, session_directory
|
43
|
+
p uploader.upload
|
44
|
+
else
|
45
|
+
@view.show_missing_command_argument_error "upload"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class FilenameFormatter
|
2
|
+
|
3
|
+
CODERSDOJO_WORKSPACE = ".codersdojo"
|
4
|
+
RESULT_FILE = "result.txt"
|
5
|
+
STATE_DIR_PREFIX = "state_"
|
6
|
+
|
7
|
+
def self.state_dir_prefix
|
8
|
+
STATE_DIR_PREFIX
|
9
|
+
end
|
10
|
+
|
11
|
+
def source_code_file state_dir
|
12
|
+
Dir.entries(state_dir).each { |file|
|
13
|
+
return state_file state_dir, file unless file =='..' || file == '.' ||file == RESULT_FILE }
|
14
|
+
end
|
15
|
+
|
16
|
+
def result_file state_dir
|
17
|
+
state_file state_dir, RESULT_FILE
|
18
|
+
end
|
19
|
+
|
20
|
+
def state_file state_dir, file
|
21
|
+
"#{state_dir}/#{file}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def state_dir session_id, step
|
25
|
+
session_directory = session_dir session_id
|
26
|
+
"#{session_directory}/#{STATE_DIR_PREFIX}#{step}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def session_dir session_id
|
30
|
+
"#{CODERSDOJO_WORKSPACE}/#{session_id}"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
data/app/progress.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class Progress
|
2
|
+
|
3
|
+
def self.write_empty_progress states
|
4
|
+
STDOUT.print "#{states} states to upload"
|
5
|
+
STDOUT.print "["+" "*states+"]"
|
6
|
+
STDOUT.print "\b"*(states+1)
|
7
|
+
STDOUT.flush
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.next
|
11
|
+
STDOUT.print "."
|
12
|
+
STDOUT.flush
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.end
|
16
|
+
STDOUT.puts
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
data/app/runner.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "filename_formatter"
|
2
|
+
|
3
|
+
class Runner
|
4
|
+
|
5
|
+
attr_accessor :file, :run_command
|
6
|
+
|
7
|
+
def initialize shell, session_provider
|
8
|
+
@filename_formatter = FilenameFormatter.new
|
9
|
+
@shell = shell
|
10
|
+
@session_provider = session_provider
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
init_session
|
15
|
+
execute
|
16
|
+
end
|
17
|
+
|
18
|
+
def init_session
|
19
|
+
@step = 0
|
20
|
+
@session_id = @session_provider.generate_id
|
21
|
+
@shell.mkdir_p(@filename_formatter.session_dir @session_id)
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute
|
25
|
+
change_time = @shell.ctime @file
|
26
|
+
if change_time == @previous_change_time then
|
27
|
+
return
|
28
|
+
end
|
29
|
+
result = @shell.execute "#{@run_command} #{@file}"
|
30
|
+
state_dir = @filename_formatter.state_dir @session_id, @step
|
31
|
+
@shell.mkdir state_dir
|
32
|
+
@shell.cp @file, state_dir
|
33
|
+
@shell.write_file @filename_formatter.result_file(state_dir), result
|
34
|
+
@step += 1
|
35
|
+
@previous_change_time = change_time
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
data/app/scheduler.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class SessionIdGenerator
|
2
|
+
|
3
|
+
def generate_id time=Time.new
|
4
|
+
year = format_to_length time.year, 4
|
5
|
+
month = format_to_length time.month, 2
|
6
|
+
day = format_to_length time.day, 2
|
7
|
+
hour = format_to_length time.hour, 2
|
8
|
+
minute = format_to_length time.min, 2
|
9
|
+
second = format_to_length time.sec, 2
|
10
|
+
"#{year}-#{month}-#{day}_#{hour}-#{minute}-#{second}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def format_to_length value, len
|
14
|
+
value.to_s.rjust len,"0"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
class ShellWrapper
|
4
|
+
|
5
|
+
MAX_STDOUT_LENGTH = 100000
|
6
|
+
ANY_TEMPLATE = "any"
|
7
|
+
|
8
|
+
def cp source, destination
|
9
|
+
FileUtils.cp source, destination
|
10
|
+
end
|
11
|
+
|
12
|
+
def mkdir dir
|
13
|
+
FileUtils.mkdir dir
|
14
|
+
end
|
15
|
+
|
16
|
+
def mkdir_p dirs
|
17
|
+
FileUtils.mkdir_p dirs
|
18
|
+
end
|
19
|
+
|
20
|
+
def execute command
|
21
|
+
spec_pipe = IO.popen(command, "r")
|
22
|
+
result = spec_pipe.read MAX_STDOUT_LENGTH
|
23
|
+
spec_pipe.close
|
24
|
+
puts result
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def write_file filename, content
|
29
|
+
file = File.new filename, "w"
|
30
|
+
file << content
|
31
|
+
file.close
|
32
|
+
end
|
33
|
+
|
34
|
+
def read_file filename
|
35
|
+
file = File.new filename
|
36
|
+
content = file.read
|
37
|
+
file.close
|
38
|
+
content
|
39
|
+
end
|
40
|
+
|
41
|
+
def ctime filename
|
42
|
+
File.new(filename).ctime
|
43
|
+
end
|
44
|
+
|
45
|
+
def list_templates
|
46
|
+
templates = real_dir_entries template_path
|
47
|
+
templates.delete ANY_TEMPLATE
|
48
|
+
templates.join(', ')
|
49
|
+
end
|
50
|
+
|
51
|
+
def scaffold template
|
52
|
+
begin
|
53
|
+
dir_path = "#{template_path}/#{template}"
|
54
|
+
to_copy = real_dir_entries dir_path
|
55
|
+
rescue
|
56
|
+
dir_path = "#{template_path}/#{ANY_TEMPLATE}"
|
57
|
+
to_copy = real_dir_entries dir_path
|
58
|
+
end
|
59
|
+
to_copy.each do |item|
|
60
|
+
FileUtils.cp_r "#{dir_path}/#{item}", "."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def template_path
|
65
|
+
file_path_elements = __FILE__.split '/'
|
66
|
+
file_path_elements[-2..-1] = nil
|
67
|
+
(file_path_elements << 'templates').join '/'
|
68
|
+
end
|
69
|
+
|
70
|
+
def real_dir_entries dir
|
71
|
+
current_and_parent = 2
|
72
|
+
Dir.new(dir).entries.drop current_and_parent
|
73
|
+
end
|
74
|
+
|
75
|
+
def make_os_specific text
|
76
|
+
text.gsub('%sh%', shell_extension).gsub('%:%', path_separator).gsub('%rm%', remove_command_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def remove_command_name
|
80
|
+
windows? ? 'delete' : 'rm'
|
81
|
+
end
|
82
|
+
|
83
|
+
def shell_extension
|
84
|
+
windows? ? 'cmd' : 'sh'
|
85
|
+
end
|
86
|
+
|
87
|
+
def path_separator
|
88
|
+
windows? ? ';' : ':'
|
89
|
+
end
|
90
|
+
|
91
|
+
def windows?
|
92
|
+
RUBY_PLATFORM.downcase.include? "windows"
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
data/app/state_reader.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'filename_formatter'
|
2
|
+
|
3
|
+
class StateReader
|
4
|
+
|
5
|
+
attr_accessor :session_id, :next_step
|
6
|
+
|
7
|
+
def initialize shell
|
8
|
+
@filename_formatter = FilenameFormatter.new
|
9
|
+
@shell = shell
|
10
|
+
@next_step = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def state_count
|
14
|
+
dummy_dirs_current_and_parent = 2
|
15
|
+
Dir.new(@filename_formatter.session_dir @session_id).count - dummy_dirs_current_and_parent
|
16
|
+
end
|
17
|
+
|
18
|
+
def enough_states?
|
19
|
+
state_count >= states_needed_for_one_move
|
20
|
+
end
|
21
|
+
|
22
|
+
def states_needed_for_one_move
|
23
|
+
2
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_state_dir
|
27
|
+
@filename_formatter.state_dir(@session_id, @next_step)
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_next_state
|
31
|
+
File.exist?(get_state_dir)
|
32
|
+
end
|
33
|
+
|
34
|
+
def read_next_state
|
35
|
+
state = State.new
|
36
|
+
state_dir = get_state_dir
|
37
|
+
state.time = @shell.ctime state_dir
|
38
|
+
state.code = @shell.read_file @filename_formatter.source_code_file(state_dir)
|
39
|
+
state.result = @shell.read_file @filename_formatter.result_file(state_dir)
|
40
|
+
@next_step += 1
|
41
|
+
state
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
class State
|
47
|
+
|
48
|
+
attr_accessor :time, :code, :result
|
49
|
+
|
50
|
+
def initialize time=nil, code=nil, result=nil
|
51
|
+
@time = time
|
52
|
+
@code = code
|
53
|
+
@result = result
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
data/app/uploader.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'state_reader'
|
2
|
+
require 'progress'
|
3
|
+
require 'xml_element_extractor'
|
4
|
+
require 'rest_client'
|
5
|
+
|
6
|
+
class Uploader
|
7
|
+
|
8
|
+
def initialize hostname, framework, session_dir, state_reader = StateReader.new(ShellWrapper.new)
|
9
|
+
@hostname = hostname
|
10
|
+
@framework = framework
|
11
|
+
@state_reader = state_reader
|
12
|
+
@state_reader.session_id = session_dir.gsub('.codersdojo/', '')
|
13
|
+
end
|
14
|
+
|
15
|
+
def upload_kata
|
16
|
+
RestClient.post "#{@hostname}#{@@kata_path}", {:framework => @framework}
|
17
|
+
end
|
18
|
+
|
19
|
+
def upload_state kata_id
|
20
|
+
state = @state_reader.read_next_state
|
21
|
+
RestClient.post "#{@hostname}#{@@kata_path}/#{kata_id}#{@@state_path}", {:code => state.code, :result => state.result, :created_at => state.time}
|
22
|
+
Progress.next
|
23
|
+
end
|
24
|
+
|
25
|
+
def upload_states kata_id
|
26
|
+
Progress.write_empty_progress @state_reader.state_count
|
27
|
+
while @state_reader.has_next_state
|
28
|
+
upload_state kata_id
|
29
|
+
end
|
30
|
+
Progress.end
|
31
|
+
end
|
32
|
+
|
33
|
+
def upload_kata_and_states
|
34
|
+
kata = upload_kata
|
35
|
+
upload_states(XMLElementExtractor.extract('kata/id', kata))
|
36
|
+
"This is the link to review and comment your kata #{XMLElementExtractor.extract('kata/short-url', kata)}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def upload
|
40
|
+
return upload_kata_and_states if @state_reader.enough_states?
|
41
|
+
return "You need at least two states"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
@@kata_path = '/katas'
|
46
|
+
@@state_path = '/states'
|
47
|
+
|
48
|
+
end
|
49
|
+
|
data/bin/codersdojo
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
codersdojo_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'app'))
|
3
|
+
infrastructure_dir = "#{codersdojo_dir}/infrastructure"
|
4
|
+
models_dir = "#{codersdojo_dir}/models"
|
3
5
|
$LOAD_PATH.unshift(codersdojo_dir) unless $LOAD_PATH.include?(codersdojo_dir)
|
6
|
+
$LOAD_PATH.unshift(infrastructure_dir) unless $LOAD_PATH.include?(infrastructure_dir)
|
7
|
+
$LOAD_PATH.unshift(models_dir) unless $LOAD_PATH.include?(models_dir)
|
4
8
|
require 'codersdojo'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'argument_parser'
|
2
|
+
|
3
|
+
describe ArgumentParser do
|
4
|
+
|
5
|
+
before (:each) do
|
6
|
+
@controller_mock = mock.as_null_object
|
7
|
+
@parser = ArgumentParser.new @controller_mock
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should reject empty command" do
|
11
|
+
lambda{@parser.parse []}.should raise_error
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should reject unknown command" do
|
15
|
+
lambda{@parser.parse "unknown command"}.should raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should accept help command" do
|
19
|
+
@controller_mock.should_receive(:help).with(nil)
|
20
|
+
@parser.parse ["help"]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should accept start command" do
|
24
|
+
@controller_mock.should_receive(:start).with "aCommand", "aFile"
|
25
|
+
@parser.parse ["start", "aCommand","aFile"]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should prepend *.sh start scripts with 'bash'" do
|
29
|
+
@controller_mock.should_receive(:start).with "bash aCommand.sh", "aFile"
|
30
|
+
@parser.parse ["start", "aCommand.sh","aFile"]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should prepend *.bat start scripts with 'start'" do
|
34
|
+
@controller_mock.should_receive(:start).with "start aCommand.bat", "aFile"
|
35
|
+
@parser.parse ["start", "aCommand.bat","aFile"]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should prepend *.cmd start scripts with 'start'" do
|
39
|
+
@controller_mock.should_receive(:start).with "start aCommand.cmd", "aFile"
|
40
|
+
@parser.parse ["start", "aCommand.cmd","aFile"]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should accept upload command" do
|
44
|
+
@controller_mock.should_receive(:upload).with "framework", "dir"
|
45
|
+
@parser.parse ["upload", "framework", "dir"]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should accept uppercase commands" do
|
49
|
+
@controller_mock.should_receive(:help).with(nil)
|
50
|
+
@parser.parse ["HELP"]
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'progress'
|
2
|
+
|
3
|
+
describe Progress do
|
4
|
+
|
5
|
+
it 'should print infos and empty progress in initialization' do
|
6
|
+
STDOUT.should_receive(:print).with("2 states to upload")
|
7
|
+
STDOUT.should_receive(:print).with("[ ]")
|
8
|
+
STDOUT.should_receive(:print).with("\b\b\b")
|
9
|
+
Progress.write_empty_progress 2
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should print dots and flush in next' do
|
13
|
+
STDOUT.should_receive(:print).with(".")
|
14
|
+
STDOUT.should_receive(:flush)
|
15
|
+
Progress.next
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should print empty line in end' do
|
19
|
+
STDOUT.should_receive(:puts)
|
20
|
+
Progress.end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/spec/runner_spec.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'runner'
|
2
|
+
|
3
|
+
describe Runner, "in run mode" do
|
4
|
+
|
5
|
+
WORKSPACE_DIR = ".codersdojo"
|
6
|
+
SESSION_ID = "id0815"
|
7
|
+
SESSION_DIR = "#{WORKSPACE_DIR}/#{SESSION_ID}"
|
8
|
+
STATE_DIR_PREFIX = "#{SESSION_DIR}/state_"
|
9
|
+
|
10
|
+
before (:each) do
|
11
|
+
@shell_mock = mock.as_null_object
|
12
|
+
@session_id_provider_mock = mock.as_null_object
|
13
|
+
@session_id_provider_mock.should_receive(:generate_id).and_return SESSION_ID
|
14
|
+
@runner = Runner.new @shell_mock, @session_id_provider_mock
|
15
|
+
@runner.file = "my_file.rb"
|
16
|
+
@runner.run_command = "ruby"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should create codersdojo directory if it doesn't exist with session sub-directory" do
|
20
|
+
@shell_mock.should_receive(:mkdir_p).with SESSION_DIR
|
21
|
+
@runner.start
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should run ruby command on kata file given as argument" do
|
25
|
+
@shell_mock.should_receive(:execute).with "ruby my_file.rb"
|
26
|
+
@runner.start
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should create a state directory for every state" do
|
30
|
+
@shell_mock.should_receive(:ctime).with("my_file.rb").and_return 1
|
31
|
+
@shell_mock.should_receive(:mkdir).with "#{STATE_DIR_PREFIX}0"
|
32
|
+
@shell_mock.should_receive(:cp).with "my_file.rb", "#{STATE_DIR_PREFIX}0"
|
33
|
+
@runner.start
|
34
|
+
@shell_mock.should_receive(:ctime).with("my_file.rb").and_return 2
|
35
|
+
@shell_mock.should_receive(:mkdir).with "#{STATE_DIR_PREFIX}1"
|
36
|
+
@shell_mock.should_receive(:cp).with "my_file.rb", "#{STATE_DIR_PREFIX}1"
|
37
|
+
@runner.execute
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not run if the kata file wasn't modified" do
|
41
|
+
a_time = Time.new
|
42
|
+
@shell_mock.should_receive(:ctime).with("my_file.rb").and_return a_time
|
43
|
+
@shell_mock.should_receive(:mkdir).with "#{STATE_DIR_PREFIX}0"
|
44
|
+
@shell_mock.should_receive(:cp).with "my_file.rb", "#{STATE_DIR_PREFIX}0"
|
45
|
+
@runner.start
|
46
|
+
@shell_mock.should_receive(:ctime).with("my_file.rb").and_return a_time
|
47
|
+
@shell_mock.should_not_receive(:mkdir).with "#{STATE_DIR_PREFIX}1"
|
48
|
+
@shell_mock.should_not_receive(:cp).with "my_file.rb", "#{STATE_DIR_PREFIX}1"
|
49
|
+
@runner.execute
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should capture run result into state directory" do
|
53
|
+
@shell_mock.should_receive(:execute).and_return "spec result"
|
54
|
+
@shell_mock.should_receive(:write_file).with "#{STATE_DIR_PREFIX}0/result.txt", "spec result"
|
55
|
+
@runner.start
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'session_id_generator'
|
2
|
+
|
3
|
+
describe SessionIdGenerator do
|
4
|
+
|
5
|
+
before (:each) do
|
6
|
+
@time_mock = mock
|
7
|
+
@generator = SessionIdGenerator.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should format id as yyyy-mm-dd_hh-mm-ss" do
|
11
|
+
@time_mock.should_receive(:year).and_return 2010
|
12
|
+
@time_mock.should_receive(:month).and_return 8
|
13
|
+
@time_mock.should_receive(:day).and_return 7
|
14
|
+
@time_mock.should_receive(:hour).and_return 6
|
15
|
+
@time_mock.should_receive(:min).and_return 5
|
16
|
+
@time_mock.should_receive(:sec).and_return 0
|
17
|
+
@generator.generate_id(@time_mock).should == "2010-08-07_06-05-00"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|