tork 18.2.4 → 19.0.0

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.
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