tork 18.2.4 → 19.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/HISTORY.markdown +65 -0
  2. data/README.markdown +60 -298
  3. data/bin/tork +99 -62
  4. data/bin/tork-driver +124 -25
  5. data/bin/tork-engine +45 -23
  6. data/bin/tork-herald +5 -5
  7. data/bin/tork-master +79 -32
  8. data/bin/tork-notify +70 -0
  9. data/bin/tork-remote +80 -0
  10. data/lib/tork/cliapp.rb +73 -0
  11. data/lib/tork/config.rb +14 -97
  12. data/lib/tork/config/coverage/master.rb +29 -0
  13. data/lib/tork/config/coverage/worker.rb +1 -0
  14. data/lib/tork/config/cucumber/driver.rb +11 -0
  15. data/lib/tork/config/cucumber/worker.rb +14 -0
  16. data/lib/tork/config/default/config.rb +5 -0
  17. data/lib/tork/config/dotlog/onfork.rb +2 -0
  18. data/lib/tork/config/factory_girl/onfork.rb +2 -0
  19. data/lib/tork/config/factory_girl/worker.rb +1 -0
  20. data/lib/tork/config/logdir/onfork.rb +4 -0
  21. data/lib/tork/config/parallel_tests/worker.rb +4 -0
  22. data/lib/tork/config/rails/driver.rb +15 -0
  23. data/lib/tork/config/rails/master.rb +12 -0
  24. data/lib/tork/config/rails/worker.rb +21 -0
  25. data/lib/tork/config/spec/driver.rb +17 -0
  26. data/lib/tork/config/spec/master.rb +3 -0
  27. data/lib/tork/config/spec/worker.rb +3 -0
  28. data/lib/tork/config/test/driver.rb +17 -0
  29. data/lib/tork/config/test/master.rb +3 -0
  30. data/lib/tork/config/test/worker.rb +23 -0
  31. data/lib/tork/driver.rb +68 -36
  32. data/lib/tork/engine.rb +57 -44
  33. data/lib/tork/master.rb +34 -34
  34. data/lib/tork/server.rb +118 -17
  35. data/lib/tork/version.rb +1 -1
  36. data/man/man1/tork-driver.1 +165 -37
  37. data/man/man1/tork-engine.1 +50 -32
  38. data/man/man1/tork-herald.1 +5 -8
  39. data/man/man1/tork-master.1 +95 -42
  40. data/man/man1/tork-notify.1 +27 -0
  41. data/man/man1/tork-remote.1 +38 -0
  42. data/man/man1/tork.1 +116 -8
  43. data/tork.gemspec +3 -3
  44. metadata +36 -25
  45. data/lib/tork/client.rb +0 -53
  46. data/lib/tork/config/coverage.rb +0 -40
  47. data/lib/tork/config/cucumber.rb +0 -32
  48. data/lib/tork/config/dotlog.rb +0 -8
  49. data/lib/tork/config/factory_girl.rb +0 -10
  50. data/lib/tork/config/logdir.rb +0 -10
  51. data/lib/tork/config/notify.rb +0 -34
  52. data/lib/tork/config/parallel_tests.rb +0 -9
  53. data/lib/tork/config/rails.rb +0 -39
data/bin/tork-herald CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  =begin =======================================================================
3
3
 
4
- # TORK-HERALD 1 2012-10-10 18.2.4
4
+ # TORK-HERALD 1 2012-10-17 19.0.0
5
5
 
6
6
  ## NAME
7
7
 
@@ -13,9 +13,9 @@ tork-herald - reports modified files
13
13
 
14
14
  ## DESCRIPTION
15
15
 
16
- This program monitors the current working directory and prints relative paths
17
- of modified files in batches of single-line JSON arrays to the standard output
18
- stream.
16
+ This program monitors the current working directory and all those below it
17
+ recursively. When any files therein are modified, it prints their relative
18
+ paths in a single-line JSON array to stdout.
19
19
 
20
20
  ## OPTIONS
21
21
 
@@ -24,7 +24,7 @@ stream.
24
24
 
25
25
  ## SEE ALSO
26
26
 
27
- tork(1), tork-herald(1), tork-driver(1), tork-engine(1), tork-master(1)
27
+ tork(1), tork-driver(1)
28
28
 
29
29
  =end =========================================================================
30
30
 
data/bin/tork-master CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  =begin =======================================================================
3
3
 
4
- # TORK-MASTER 1 2012-10-10 18.2.4
4
+ # TORK-MASTER 1 2012-10-17 19.0.0
5
5
 
6
6
  ## NAME
7
7
 
@@ -13,39 +13,47 @@ tork-master - absorbs overhead and runs tests
13
13
 
14
14
  ## DESCRIPTION
15
15
 
16
- This program absorbs the test execution overhead and forks to run your tests.
17
- It reads the following single-line commands (JSON arrays) from its standard
18
- input stream and performs the respective actions as described below.
16
+ This program absorbs your Ruby application's test execution overhead once and
17
+ simply fork(3)s worker processses to run your tests thereafter. As a result,
18
+ your tests run faster because they no longer spend any time absorbing the test
19
+ execution overhead: worker processes simply inherit the overhead when forked.
19
20
 
20
- `["load",` *paths*`,` *files*`]`
21
- Adds the given array of *paths* to Ruby's $LOAD_PATH, loads the given array
22
- of *files* after removing their ".rb" file extension if present, and prints
23
- the given command line to the standard output stream.
21
+ This program can be controlled remotely by multiple tork-remote(1) instances.
22
+
23
+ ### Input
24
+
25
+ This program reads the following commands, which are single-line JSON arrays,
26
+ from stdin and performs the actions described respectively.
24
27
 
25
28
  `["test",` *test_file*`,` *line_numbers*`]`
26
- Runs the given *test_file* in a forked child process while instructing your
27
- chosen unit testing framework (loaded by your test execution overhead) to
28
- only run those tests that are defined on the given array of *line_numbers*.
29
+ Forks a worker process to run tests that correspond to the given
30
+ *line_numbers* in the given *test_file*. If *line_numbers* is empty, then
31
+ the entire *test_file* will be run.
32
+
33
+ `["stop",` *signal*`]`
34
+ Stops all tests that are currently running by sending the given *signal*
35
+ (optional; defaults to "SIGTERM") to their respective worker processes.
36
+
37
+ `["quit"]`
38
+ Stops all tests that are currently running and exits.
29
39
 
30
- Prints the following status messages to the standard output stream. The
31
- standard output and error streams of the forked child process are captured
32
- in the *log_file* specified in these status messages.
40
+ ### Output
33
41
 
34
- * Test is running:
35
- `["test",` *test_file*`,` *line_numbers*`,` *log_file*`,` *worker_number*`]`
42
+ This program prints the following messages, which are single-line JSON arrays,
43
+ to stdout.
36
44
 
37
- * Test has passed:
38
- `["pass",` *test_file*`,` *line_numbers*`,` *log_file*`,` *worker_number*`,` *exit_code*`,` *exit_info*`]`
45
+ `["absorb"]`
46
+ Test execution overhead has been absorbed. We are ready for testing!
39
47
 
40
- * Test has failed:
41
- `["fail",` *test_file*`,` *line_numbers*`,` *log_file*`,` *worker_number*`,` *exit_code*`,` *exit_info*`]`
48
+ `["test",` *test_file*`,` *line_numbers*`,` *log_file*`,` *worker_number*`]`
49
+ Test has begun running. Its output (both stdout and stderr) is being
50
+ captured into *log_file* in real time, so you can watch it with `tail -f`.
42
51
 
43
- `["stop"]`
44
- Stops all tests that are currently running and prints the given command line
45
- to the standard output stream.
52
+ `["pass",` *test_file*`,` *line_numbers*`,` *log_file*`,` *worker_number*`,` *exit_code*`,` *exit_info*`]`
53
+ Test has passed.
46
54
 
47
- `["quit"]`
48
- Stops all tests that are currently running and exits.
55
+ `["fail",` *test_file*`,` *line_numbers*`,` *log_file*`,` *worker_number*`,` *exit_code*`,` *exit_info*`]`
56
+ Test has failed.
49
57
 
50
58
  ## OPTIONS
51
59
 
@@ -54,19 +62,58 @@ input stream and performs the respective actions as described below.
54
62
 
55
63
  ## FILES
56
64
 
57
- *.tork.rb*
58
- Optional Ruby script for configuring tork(1).
65
+ *.tork/config.rb*
66
+ Optional Ruby script that is loaded inside the driver process on startup.
67
+ It can read and change the `ENV['TORK_CONFIGS']` environment variable.
68
+
69
+ *.tork/master.rb*
70
+ Optional Ruby script that is loaded inside the master process on startup.
71
+ It can read and change the following variables.
72
+
73
+ > `Tork::Master::MAX_CONCURRENT_WORKERS`
74
+ > Maximum number of worker processes that are allowed to be running
75
+ > simultaneously at any given time. The default value is either the
76
+ > number of processors detected on your system or 1 if detection failed.
77
+
78
+ *.tork/onfork.rb*
79
+ Optional Ruby script that is loaded inside the master process just before a
80
+ worker process is forked. It can read and change the following variables.
81
+
82
+ > `$tork_test_file`
83
+ > Path of the test file that will be run by the worker process.
84
+ >
85
+ > `$tork_line_numbers`
86
+ > Array of line numbers in the test file that were requested to be run.
87
+ >
88
+ > `$tork_log_file`
89
+ > Path of the log file that will hold the output of the worker process.
90
+ >
91
+ > `$tork_worker_number`
92
+ > Sequence number of the worker process that will be forked shortly.
93
+
94
+ *.tork/worker.rb*
95
+ Optional Ruby script that is loaded inside a worker process just after
96
+ it is forked. It can read and change the following variables.
97
+
98
+ > `$tork_test_file`
99
+ > Path of the test file that will be run by this worker process.
100
+ >
101
+ > `$tork_line_numbers`
102
+ > Array of line numbers in the test file that were requested to be run.
103
+ >
104
+ > `$tork_log_file`
105
+ > Path of the log file that will hold the output of this worker process.
106
+ >
107
+ > `$tork_worker_number`
108
+ > Sequence number of this worker process.
59
109
 
60
110
  ## ENVIRONMENT
61
111
 
62
- `TORK_CONFIGS`
63
- A single-line JSON array containing paths to actual files or names of
64
- helper libraries in the tork/config/ namespace of Ruby's load path.
65
- These configuration files are loaded just before *.tork.rb* is loaded.
112
+ See tork(1).
66
113
 
67
114
  ## SEE ALSO
68
115
 
69
- tork(1), tork-herald(1), tork-driver(1), tork-engine(1), tork-master(1)
116
+ tork(1), tork-remote(1)
70
117
 
71
118
  =end =========================================================================
72
119
 
data/bin/tork-notify ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+ =begin =======================================================================
3
+
4
+ # TORK-NOTIFY 1 2012-10-17 19.0.0
5
+
6
+ ## NAME
7
+
8
+ tork-notify - notifies you of test status changes
9
+
10
+ ## SYNOPSIS
11
+
12
+ `tork-notify` [*OPTION*]...
13
+
14
+ ## DESCRIPTION
15
+
16
+ This program serves as an example of how to receive and process messages sent
17
+ by the various programs in the tork(1) suite. It notifies you when previously
18
+ passing tests fail (or vice versa) through libnotify, xmessage, or growl. If
19
+ none are available on your system, then the notification is printed to stdout.
20
+
21
+ ## OPTIONS
22
+
23
+ `-h`, `--help`
24
+ Show this help manual.
25
+
26
+ ## EXIT STATUS
27
+
28
+ See tork-remote(1).
29
+
30
+ ## SEE ALSO
31
+
32
+ tork-remote(1), tork-engine(1)
33
+
34
+ =end =========================================================================
35
+
36
+ $0 = File.basename(__FILE__) # for easier identification in ps(1) output
37
+
38
+ require 'binman'
39
+ BinMan.help
40
+
41
+ require 'json'
42
+ IO.popen('tork-remote tork-engine', 'r+') do |remote|
43
+ while message = remote.gets
44
+ event, test_file, *details = JSON.load(message)
45
+
46
+ # make notifications edge-triggered: pass => fail or vice versa.
47
+ # we do not care about pass => pass or fail => fail transitions.
48
+ icon = case event.to_sym
49
+ when :fail_now_pass then 'dialog-information'
50
+ when :pass_now_fail then 'dialog-error'
51
+ end
52
+
53
+ if icon
54
+ _, _, line_numbers, log_file = details.first
55
+
56
+ title = [event.upcase, test_file].join(' ')
57
+
58
+ statistics = File.readlines(log_file).grep(/^\d+ \w+,/).join.
59
+ gsub(/\e\[\d+(;\d+)?m/, '') # strip ANSI SGR escape codes
60
+
61
+ Thread.new do # run in background
62
+ system 'notify-send', '-i', icon, title, statistics or
63
+ system 'growlnotify', '-a', 'Xcode', '-m', statistics, title or
64
+ system 'xmessage', '-timeout', '5', '-title', title, statistics or
65
+ puts title, statistics, nil
66
+ end
67
+ end
68
+ end
69
+ end
70
+ exit $?.exitstatus
data/bin/tork-remote ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+ =begin =======================================================================
3
+
4
+ # TORK-REMOTE 1 2012-09-26 18.2.3
5
+
6
+ ## NAME
7
+
8
+ tork-remote - controls tork(1) programs
9
+
10
+ ## SYNOPSIS
11
+
12
+ `tork-remote` [*OPTION*]... *PROGRAM*
13
+
14
+ ## DESCRIPTION
15
+
16
+ This program sends single-line JSON messages read from its stdin to the given
17
+ *PROGRAM* which is already running in the same working directory as this one.
18
+
19
+ If lines read from stdin are not single-line JSON messages, then they are
20
+ split into an array of words, using the same word-splitting algorithm as
21
+ sh(1), before being sent to the *PROGRAM* as a single-line JSON message.
22
+
23
+ If the *PROGRAM* sends any messages in response, then they are printed to
24
+ stdout if they are valid single-line JSON messages or to stderr otherwise.
25
+
26
+ ## OPTIONS
27
+
28
+ `-h`, `--help`
29
+ Show this help manual.
30
+
31
+ ## EXIT STATUS
32
+
33
+ 1
34
+ Could not connect to the *PROGRAM*.
35
+
36
+ 2
37
+ Lost connection to the *PROGRAM*.
38
+
39
+ ## SEE ALSO
40
+
41
+ tork(1), tork-driver(1), tork-engine(1), tork-master(1)
42
+
43
+ =end =========================================================================
44
+
45
+ $0 = File.basename(__FILE__) # for easier identification in ps(1) output
46
+
47
+ require 'binman'
48
+ BinMan.help
49
+
50
+ require 'socket'
51
+ require 'shellwords'
52
+ require 'tork/server'
53
+
54
+ program = ARGV.shift or raise ArgumentError, 'PROGRAM not given'
55
+ address = Tork::Server.address(program)
56
+
57
+ begin
58
+ UNIXSocket.open(address) do |socket|
59
+ # messages to remote from server
60
+ Thread.new do
61
+ while input = socket.gets
62
+ stream = input =~ Tork::Server::JSON_REGEXP ? STDOUT : STDERR
63
+ stream.puts input
64
+ stream.flush
65
+ end
66
+ warn "#{$0}: lost connection to #{program}"
67
+ exit 2
68
+ end
69
+
70
+ # messages from remote to server
71
+ while output = STDIN.gets
72
+ socket.puts output
73
+ end
74
+ end
75
+ # ECONNREFUSED is for abstract namespace UNIXSocket
76
+ rescue Errno::ENOENT, Errno::ECONNREFUSED => error
77
+ warn "#{$0}: could not connect to #{program}"
78
+ warn "#{error}#{address.inspect}"
79
+ exit 1
80
+ end
@@ -0,0 +1,73 @@
1
+ require 'tork/server'
2
+
3
+ module Tork
4
+ class CLIApp < Server
5
+
6
+ COMMANDS = {
7
+ 't' => :run_test_file,
8
+ 'a' => :run_all_test_files,
9
+ 's' => :stop_running_test_files,
10
+ 'k' => [:stop_running_test_files, :SIGKILL],
11
+ 'p' => :rerun_passed_test_files,
12
+ 'f' => :rerun_failed_test_files,
13
+ 'o' => :reabsorb_overhead,
14
+ 'q' => :quit,
15
+ }
16
+
17
+ def loop
18
+ tell @clients, 'Absorbing test execution overhead...', false
19
+ @driver = popen('tork-driver')
20
+ super
21
+ ensure
22
+ pclose @driver
23
+ end
24
+
25
+ protected
26
+
27
+ def recv client, message
28
+ case client
29
+ when @driver
30
+ event, *details = message
31
+
32
+ case event_sym = event.to_sym
33
+ when :absorb
34
+ tell @clients, 'Overhead absorbed. Ready for testing!', false
35
+
36
+ when :reabsorb
37
+ tell @clients, 'Reabsorbing changed overhead files...', false
38
+
39
+ when :test, :pass, :fail
40
+ test_file, line_numbers, log_file, worker_number, exit_status = details
41
+ message = [event.upcase, [test_file, *line_numbers].join(':'),
42
+ exit_status].compact.join(' ')
43
+
44
+ color = case event_sym
45
+ when :pass then "\e[34m%s\e[0m" # blue
46
+ when :fail then "\e[31m%s\e[0m" # red
47
+ end
48
+ message = color % message if color and STDOUT.tty?
49
+ message = [message, File.read(log_file), message] if event_sym == :fail
50
+
51
+ tell @clients, message, false
52
+ end
53
+ else
54
+ key, *args = message
55
+ key &&= key.lstrip[0,1].downcase
56
+
57
+ if cmd = COMMANDS[key]
58
+ quit if cmd == :quit
59
+ call = Array(cmd) + args
60
+ tell @clients, "Sending #{call.inspect} command...", false
61
+ send @driver, call
62
+ else
63
+ # user typed an invalid command so help them along
64
+ COMMANDS.each do |key, cmd|
65
+ desc = Array(cmd).join(' with ').to_s.tr('_', ' ')
66
+ tell @client, "Type #{key} then ENTER to #{desc}.", false
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ end
73
+ end
data/lib/tork/config.rb CHANGED
@@ -1,100 +1,17 @@
1
1
  module Tork
2
-
3
- Config = Struct.new(:max_forked_workers, :overhead_load_paths,
4
- :overhead_file_globs, :reabsorb_file_greps,
5
- :all_test_file_globs, :test_file_globbers,
6
- :before_fork_hooks, :after_fork_hooks,
7
- :test_event_hooks).new
8
-
9
- #---------------------------------------------------------------------------
10
- # defaults
11
- #---------------------------------------------------------------------------
12
-
13
- Config.max_forked_workers = [
14
- # http://stackoverflow.com/questions/891537#6420817
15
- 'fgrep -c processor /proc/cpuinfo', # Linux
16
- 'sysctl -n hw.ncpu', # BSD
17
- 'hwprefs cpu_count', # Darwin 9
18
- 'hwprefs thread_count', # Darwin 10
19
- ].map {|cmd| `#{cmd} 2>/dev/null`.to_i }.push(1).max
20
-
21
- Config.overhead_load_paths = ['lib', 'test', 'spec']
22
-
23
- Config.overhead_file_globs = ['{test,spec}/{test,spec}_helper.rb']
24
-
25
- Config.reabsorb_file_greps = [%r<^(test|spec)/\1_helper\.rb$>]
26
-
27
- Config.all_test_file_globs = ['{test,spec}/**/*_{test,spec}.rb',
28
- '{test,spec}/**/{test,spec}_*.rb']
29
-
30
- Config.test_file_globbers = {
31
- # source files that correspond to test files
32
- %r<^lib/.*?([^/]+)\.rb$> => lambda do |matches|
33
- name = matches[1]
34
- ["{test,spec}/**/#{name}_{test,spec}.rb",
35
- "{test,spec}/**/{test,spec}_#{name}.rb"]
36
- end,
37
-
38
- # the actual test files themselves
39
- %r<^(test|spec)/.*?(\1_[^/]+|[^/]+_\1)\.rb$> => lambda {|m| m[0] }
40
- }
41
-
42
- Config.before_fork_hooks = []
43
-
44
- Config.after_fork_hooks = [
45
- # instruct the testing framework to only run those
46
- # tests that are defined on the given line numbers
47
- lambda do |test_file, line_numbers, log_file, worker_number|
48
- case File.basename(test_file)
49
- when /(\b|_)spec(\b|_).*\.rb$/ # RSpec
50
- line_numbers.each {|line| ARGV.push '--line_number', line.to_s }
51
-
52
- when /(\b|_)test(\b|_).*\.rb$/ # Test::Unit
53
- # find which tests have changed inside the test file
54
- test_file_lines = File.readlines(test_file)
55
- test_names = line_numbers.map do |line|
56
- catch :found do
57
- # search backwards from the line that changed up to
58
- # the first line in the file for test definitions
59
- line.downto(0) do |i|
60
- test_name =
61
- case test_file_lines[i]
62
- when /^\s*def\s+test_(\w+)/ then $1
63
- when /^\s*(test|context|should|describe|it)\b.+?(['"])(.*?)\2/
64
- # elide string interpolation and invalid method name characters
65
- $3.gsub(/\#\{.*?\}/, ' ').strip.gsub(/\W+/, '.*')
66
- end \
67
- and throw :found, test_name
68
- end; nil # prevent unsuccessful search from returning an integer
69
- end
70
- end.compact.uniq
71
-
72
- unless test_names.empty?
73
- ARGV.push '--name', "/(?i:#{test_names.join('|')})/"
74
- end
75
- end
76
- end
77
- ]
78
-
79
- Config.test_event_hooks = []
80
-
81
- #---------------------------------------------------------------------------
82
- # overrides
83
- #---------------------------------------------------------------------------
84
-
85
- if File.exist? user_config_file = '.tork.rb'
86
- load user_config_file
87
- end
88
-
89
- if ENV.key? 'TORK_CONFIGS'
90
- require 'json'
91
- JSON.load(ENV['TORK_CONFIGS']).each do |config|
92
- if File.file? config
93
- load File.expand_path(config)
94
- else
95
- require "tork/config/#{config}"
96
- end
97
- end
2
+ # Loads all Ruby scripts found having the given name in (1) the directories
3
+ # specified in the TORK_CONFIGS environment variable, (2) the subdirectories
4
+ # of lib/tork/config/, and (3) the user's .tork/ directory; in that order.
5
+ #
6
+ # @return [Array] paths of Ruby scripts that were loaded
7
+ #
8
+ def self.config name
9
+ dirs = ENV['TORK_CONFIGS'].strip.split(/:+/).reject(&:empty?).uniq.
10
+ map {|dir| [dir, __FILE__.sub(/\.rb$/, "/#{dir}")] }.flatten
11
+
12
+ Dir["{#{dirs.join(',')},.tork}/#{name}.rb"].each {|script| load script }
98
13
  end
99
-
100
14
  end
15
+
16
+ ENV['TORK_CONFIGS'] ||= 'default'.freeze # ENV values come frozen by default
17
+ Tork.config :config