spinoff 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/README.md +89 -0
- data/Rakefile +2 -0
- data/bin/spinoff-client +8 -0
- data/bin/spinoff-server +36 -0
- data/lib/spinoff/client.rb +22 -0
- data/lib/spinoff/server.rb +14 -0
- data/lib/spinoff/server/fork.rb +39 -0
- data/lib/spinoff/server/generic.rb +49 -0
- data/lib/spinoff/server/jruby.rb +44 -0
- data/lib/spinoff/socket_path.rb +8 -0
- data/lib/spinoff/test_runner.rb +16 -0
- data/lib/spinoff/test_runner/rspec.rb +17 -0
- data/lib/spinoff/test_runner/test_unit.rb +9 -0
- data/lib/spinoff/version.rb +3 -0
- data/spinoff.gemspec +17 -0
- metadata +64 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Spinoff
|
2
|
+
|
3
|
+
Spinoff will help you to speed up your Ruby test workflow.
|
4
|
+
|
5
|
+
It does that by preloading your environment and then using functions like
|
6
|
+
`fork` for each run of your test suite to avoid loading framework code
|
7
|
+
like Rails over and over again. (it supports JRuby as well)
|
8
|
+
|
9
|
+
# Credits
|
10
|
+
|
11
|
+
Lots of code has been taken from the [Spin](https://github.com/jstorimer/spin)
|
12
|
+
project by Jesse Storimer. Thanks for sharing, Jesse!
|
13
|
+
|
14
|
+
# How does it work
|
15
|
+
|
16
|
+
Spinoff operates with the assumption that most of the dependencies of your
|
17
|
+
application do not change. If you are working on a Rails project, you
|
18
|
+
probably do not have to reload all Rails classes for each test run.
|
19
|
+
|
20
|
+
The `spinoff-server` command starts a server process that will listen for
|
21
|
+
a list of test files on a unix domain socket. It will also load an init file
|
22
|
+
which contains all code that should be preloaded.
|
23
|
+
(like `require 'config/application'` for Rails)
|
24
|
+
|
25
|
+
The `spinoff-client` command will be called with a list of test files that
|
26
|
+
should be executed. It sends that file list to the server process via the
|
27
|
+
unix domain socket.
|
28
|
+
|
29
|
+
Once the server process receives a list of files, it will fork the Ruby process
|
30
|
+
and execute the files with the selected test runner. All code that changes
|
31
|
+
frequently will only be loaded in the forked process.
|
32
|
+
|
33
|
+
This allows Spinoff to speed up our test suite without any knowledge of the
|
34
|
+
frameworks and libraries we use to build our app.
|
35
|
+
|
36
|
+
# How is it different from Spin?
|
37
|
+
|
38
|
+
Spinoff is based on [Spin](https://github.com/jstorimer/spin) but differs on
|
39
|
+
the following points.
|
40
|
+
|
41
|
+
1. It is framework agnostic and thus needs an init file to preload code.
|
42
|
+
Spin does only support Rails at this time of writing.
|
43
|
+
|
44
|
+
2. It works with [JRuby](http://www.jruby.org/)!
|
45
|
+
|
46
|
+
# Usage
|
47
|
+
|
48
|
+
Spinoff needs an init file to preload code. Please make sure you only load
|
49
|
+
libraries that do not change between your test runs. If you change anything
|
50
|
+
you preload in the init file, you have to restart the Spinoff server.
|
51
|
+
|
52
|
+
Example:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
require 'config/application'
|
56
|
+
require 'rspec'
|
57
|
+
```
|
58
|
+
|
59
|
+
Starting the server:
|
60
|
+
|
61
|
+
$ spinoff-server --rspec config/spinoff.rb
|
62
|
+
|
63
|
+
Please use `spinoff-server --help` to show all available options.
|
64
|
+
|
65
|
+
Sending a list of test files to the server:
|
66
|
+
|
67
|
+
$ spinoff-client spec/test1_spec.rb spec/foo/bar_spec.rb
|
68
|
+
|
69
|
+
Spinoff should work well with autotest(ish) tools. Just start the server and
|
70
|
+
execute the spinoff client on file changes.
|
71
|
+
|
72
|
+
# JRuby Support
|
73
|
+
|
74
|
+
Since JRuby does not support the fork system call, Spinoff is using a
|
75
|
+
new `ScriptingContainer` for each test run. That also means we cannot really
|
76
|
+
preload any code. But it saves us from starting a new JVM for each test run.
|
77
|
+
|
78
|
+
# Contributing
|
79
|
+
|
80
|
+
I'm happy about any kind of feedback and/or contribution.
|
81
|
+
|
82
|
+
# Caveats
|
83
|
+
|
84
|
+
* New and not well tested.
|
85
|
+
* No test suite yet.
|
86
|
+
|
87
|
+
# Author
|
88
|
+
|
89
|
+
[Bernd Ahlers](https://github.com/bernd)
|
data/Rakefile
ADDED
data/bin/spinoff-client
ADDED
data/bin/spinoff-server
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'optparse'
|
5
|
+
require 'spinoff/server'
|
6
|
+
|
7
|
+
config = {}
|
8
|
+
|
9
|
+
parser = OptionParser.new do |o|
|
10
|
+
o.banner = 'Usage: spinoff-server [options] <init file>'
|
11
|
+
|
12
|
+
o.on('--rspec', 'Use rspec framework (default)') do |v|
|
13
|
+
config[:test_framework] = :rspec
|
14
|
+
end
|
15
|
+
|
16
|
+
o.on('--test-unit', 'Use test/unit framework') do |v|
|
17
|
+
config[:test_framework] = :testunit
|
18
|
+
end
|
19
|
+
|
20
|
+
o.on('-h', '--help') do
|
21
|
+
STDERR.puts o
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
parser.parse!
|
26
|
+
|
27
|
+
unless config[:test_framework]
|
28
|
+
STDERR.puts "Please select a test framework! (see --help)"
|
29
|
+
exit 1
|
30
|
+
end
|
31
|
+
|
32
|
+
config[:init_script] = ARGV.shift unless ARGV.empty?
|
33
|
+
|
34
|
+
Spinoff::Server.start(config)
|
35
|
+
|
36
|
+
exit 0
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'spinoff/socket_path'
|
3
|
+
|
4
|
+
module Spinoff
|
5
|
+
module Client
|
6
|
+
def self.start(argv)
|
7
|
+
files = argv.select {|f| File.exist?(f) }.uniq.join(File::PATH_SEPARATOR)
|
8
|
+
|
9
|
+
return if files.empty?
|
10
|
+
|
11
|
+
socket = UNIXSocket.open(Spinoff.socket_path)
|
12
|
+
socket.puts files
|
13
|
+
|
14
|
+
while line = socket.gets
|
15
|
+
break if line == "\0"
|
16
|
+
print line
|
17
|
+
end
|
18
|
+
rescue Errno::ECONNREFUSED
|
19
|
+
abort "Connection to spinoff server was refused."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Spinoff
|
2
|
+
module Server
|
3
|
+
def self.start(config)
|
4
|
+
case RUBY_PLATFORM
|
5
|
+
when /java/i
|
6
|
+
require 'spinoff/server/jruby'
|
7
|
+
Spinoff::Server::JRuby.start(config)
|
8
|
+
else
|
9
|
+
require 'spinoff/server/fork'
|
10
|
+
Spinoff::Server::Fork.start(config)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spinoff/server/generic'
|
2
|
+
require 'spinoff/test_runner'
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
module Spinoff
|
6
|
+
module Server
|
7
|
+
class Fork < Spinoff::Server::Generic
|
8
|
+
def start
|
9
|
+
test_runner = Spinoff::TestRunner.init(config[:test_framework])
|
10
|
+
load_init_script(init_script)
|
11
|
+
|
12
|
+
accept_loop do |files|
|
13
|
+
start = Time.now
|
14
|
+
|
15
|
+
fork do
|
16
|
+
STDERR.puts "Loading #{files.inspect}"
|
17
|
+
test_runner << files
|
18
|
+
end
|
19
|
+
|
20
|
+
Process.wait
|
21
|
+
|
22
|
+
STDERR.puts "Execution time: %.4fs" % (Time.now - start)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def load_init_script(file)
|
28
|
+
if File.exist?(file)
|
29
|
+
sec = Benchmark.realtime do
|
30
|
+
require File.expand_path(file)
|
31
|
+
end
|
32
|
+
STDERR.puts "Loaded init script in %.4fs (%s)" % [sec, file]
|
33
|
+
else
|
34
|
+
STDERR.puts "WARNING: Init script '#{file}' does not exist!"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'spinoff/socket_path'
|
3
|
+
|
4
|
+
module Spinoff
|
5
|
+
module Server
|
6
|
+
class Generic
|
7
|
+
def self.start(config)
|
8
|
+
new(config).start
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :config, :socket
|
12
|
+
|
13
|
+
def initialize(config)
|
14
|
+
@config = config
|
15
|
+
@socket = create_socket
|
16
|
+
end
|
17
|
+
|
18
|
+
def init_script
|
19
|
+
File.expand_path(@config.fetch(:init_script, File.join(Dir.pwd, 'spinoff.rb')))
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
raise "#{self.class}#start not implemented."
|
24
|
+
end
|
25
|
+
|
26
|
+
def accept_loop
|
27
|
+
loop do
|
28
|
+
connection = socket.accept
|
29
|
+
files = connection.gets.chomp.split(File::PATH_SEPARATOR)
|
30
|
+
|
31
|
+
disconnect_client(connection)
|
32
|
+
yield(files)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def disconnect_client(client)
|
37
|
+
client.print("\0")
|
38
|
+
client.close
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def create_socket
|
43
|
+
path = Spinoff.socket_path
|
44
|
+
File.delete(path) if File.exist?(path)
|
45
|
+
UNIXServer.open(path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'spinoff/server/generic'
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
module Spinoff
|
6
|
+
module Server
|
7
|
+
class JRuby < Spinoff::Server::Generic
|
8
|
+
import 'org.jruby.embed.PathType'
|
9
|
+
import 'org.jruby.embed.LocalContextScope'
|
10
|
+
import 'org.jruby.embed.ScriptingContainer'
|
11
|
+
|
12
|
+
def start
|
13
|
+
accept_loop do |files|
|
14
|
+
start = Time.now
|
15
|
+
|
16
|
+
STDERR.puts "Creating JRuby scripting container"
|
17
|
+
context = ScriptingContainer.new(LocalContextScope::SINGLETHREAD)
|
18
|
+
|
19
|
+
if File.exist?(init_script)
|
20
|
+
sec = Benchmark.realtime do
|
21
|
+
context.run_scriptlet(PathType::ABSOLUTE, init_script)
|
22
|
+
end
|
23
|
+
STDERR.puts "Loaded init script in %.4fs (%s)" % [sec, init_script]
|
24
|
+
else
|
25
|
+
STDERR.puts "WARNING: Init script '#{init_script}' does not exist!"
|
26
|
+
end
|
27
|
+
|
28
|
+
STDERR.puts "Loading #{files.inspect}"
|
29
|
+
context.put('framework', config[:test_framework])
|
30
|
+
context.put('files', files)
|
31
|
+
|
32
|
+
context.run_scriptlet <<-__RUBY
|
33
|
+
require 'spinoff/test_runner'
|
34
|
+
runner = Spinoff::TestRunner.init(framework)
|
35
|
+
runner << files
|
36
|
+
__RUBY
|
37
|
+
context.terminate
|
38
|
+
|
39
|
+
STDERR.puts "Execution time: %.4fs" % (Time.now - start)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Spinoff
|
2
|
+
module TestRunner
|
3
|
+
def self.init(framework)
|
4
|
+
STDERR.puts "Test framework: #{framework}"
|
5
|
+
|
6
|
+
case framework
|
7
|
+
when :rspec
|
8
|
+
require 'spinoff/test_runner/rspec'
|
9
|
+
Spinoff::TestRunner::RSpec.new
|
10
|
+
when :testunit
|
11
|
+
require 'spinoff/test_runner/test_unit'
|
12
|
+
Spinoff::TestRunner::TestUnit.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rspec/core'
|
2
|
+
|
3
|
+
module Spinoff
|
4
|
+
module TestRunner
|
5
|
+
class RSpec
|
6
|
+
def <<(files)
|
7
|
+
# Overwrite the trap_interrupt method to avoid weird behaviour
|
8
|
+
# on SIGINT.
|
9
|
+
::RSpec::Core::Runner.class_eval do
|
10
|
+
def self.trap_interrupt; end
|
11
|
+
end
|
12
|
+
|
13
|
+
::RSpec::Core::Runner.run(files)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/spinoff.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/spinoff/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Bernd Ahlers"]
|
6
|
+
gem.email = ["bernd@tuneafish.de"]
|
7
|
+
gem.description = %q{Environment preloader}
|
8
|
+
gem.summary = %q{Spinoff preloads your Ruby environment based on an initialization file.}
|
9
|
+
gem.homepage = "https://github.com/bernd/spinoff"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "spinoff"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Spinoff::VERSION
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spinoff
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bernd Ahlers
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-26 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Environment preloader
|
15
|
+
email:
|
16
|
+
- bernd@tuneafish.de
|
17
|
+
executables:
|
18
|
+
- spinoff-client
|
19
|
+
- spinoff-server
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- .gitignore
|
24
|
+
- Gemfile
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- bin/spinoff-client
|
28
|
+
- bin/spinoff-server
|
29
|
+
- lib/spinoff/client.rb
|
30
|
+
- lib/spinoff/server.rb
|
31
|
+
- lib/spinoff/server/fork.rb
|
32
|
+
- lib/spinoff/server/generic.rb
|
33
|
+
- lib/spinoff/server/jruby.rb
|
34
|
+
- lib/spinoff/socket_path.rb
|
35
|
+
- lib/spinoff/test_runner.rb
|
36
|
+
- lib/spinoff/test_runner/rspec.rb
|
37
|
+
- lib/spinoff/test_runner/test_unit.rb
|
38
|
+
- lib/spinoff/version.rb
|
39
|
+
- spinoff.gemspec
|
40
|
+
homepage: https://github.com/bernd/spinoff
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.11
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Spinoff preloads your Ruby environment based on an initialization file.
|
64
|
+
test_files: []
|