zeus-edge 0.12.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/Gemfile +4 -0
- data/MIT-LICENSE +22 -0
- data/Rakefile +57 -0
- data/bin/zeus +16 -0
- data/build/fsevents-wrapper +0 -0
- data/build/zeus-darwin-amd64 +0 -0
- data/build/zeus-linux-386 +0 -0
- data/build/zeus-linux-amd64 +0 -0
- data/ext/inotify-wrapper/extconf.rb +24 -0
- data/ext/inotify-wrapper/inotify-wrapper.cpp +116 -0
- data/lib/zeus/load_tracking.rb +53 -0
- data/lib/zeus/m/test_collection.rb +52 -0
- data/lib/zeus/m/test_method.rb +35 -0
- data/lib/zeus/m.rb +346 -0
- data/lib/zeus/plan.rb +0 -0
- data/lib/zeus/rails.rb +220 -0
- data/lib/zeus.rb +161 -0
- data/man/build/zeus +61 -0
- data/man/build/zeus-init +13 -0
- data/man/build/zeus-init.txt +17 -0
- data/man/build/zeus-start +16 -0
- data/man/build/zeus-start.txt +18 -0
- data/man/build/zeus.txt +65 -0
- data/spec/fake_mini_test.rb +31 -0
- data/spec/m_spec.rb +83 -0
- data/spec/spec_helper.rb +38 -0
- data/zeus.gemspec +33 -0
- metadata +109 -0
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Burke Libbey
|
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/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require 'fileutils'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
ROOT_PATH = Pathname.new(File.expand_path("../../", __FILE__))
|
7
|
+
RUBYGEM_PATH = Pathname.new(File.expand_path("../", __FILE__))
|
8
|
+
|
9
|
+
task build: [:import_externals, :version, :manifest]
|
10
|
+
task default: :build
|
11
|
+
|
12
|
+
task :import_externals do
|
13
|
+
puts "building rubygem"
|
14
|
+
|
15
|
+
Rake::Task[:clean].invoke
|
16
|
+
|
17
|
+
FileUtils.rm_r(RUBYGEM_PATH + "man") rescue nil
|
18
|
+
FileUtils.rm_r(RUBYGEM_PATH + "build") rescue nil
|
19
|
+
|
20
|
+
# manpages
|
21
|
+
FileUtils.mkdir(RUBYGEM_PATH + "man")
|
22
|
+
FileUtils.cp_r(ROOT_PATH + "man/build", RUBYGEM_PATH + "man")
|
23
|
+
|
24
|
+
# multi-arch binaries
|
25
|
+
FileUtils.cp_r(ROOT_PATH + "build", RUBYGEM_PATH + "build")
|
26
|
+
|
27
|
+
Rake::Task[:manifest].invoke
|
28
|
+
Rake::Task[:build].invoke
|
29
|
+
end
|
30
|
+
|
31
|
+
task :version do
|
32
|
+
version = File.read('../VERSION').chomp
|
33
|
+
File.open('lib/zeus/version.rb', 'w') { |f| f.puts <<END
|
34
|
+
module Zeus
|
35
|
+
VERSION = "#{version}"
|
36
|
+
end
|
37
|
+
END
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
task :manifest do
|
42
|
+
files = `find . -type f | sed 's|^\./||'`.lines.map(&:chomp)
|
43
|
+
exceptions = [
|
44
|
+
/.gitignore$/,
|
45
|
+
/^MANIFEST$/,
|
46
|
+
/^pkg\//,
|
47
|
+
]
|
48
|
+
files.reject! { |f| exceptions.any? {|ex| f =~ ex }}
|
49
|
+
File.open('MANIFEST', 'w') {|f| f.puts files.join("\n") }
|
50
|
+
end
|
51
|
+
|
52
|
+
task :clean do
|
53
|
+
FileUtils.rm(RUBYGEM_PATH + "lib/zeus/version.rb") rescue nil
|
54
|
+
FileUtils.rm_r(RUBYGEM_PATH + "man") rescue nil
|
55
|
+
FileUtils.rm_r(RUBYGEM_PATH + "build") rescue nil
|
56
|
+
end
|
57
|
+
|
data/bin/zeus
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
platform = `uname -sm`
|
2
|
+
|
3
|
+
exe = case platform
|
4
|
+
when /^Darwin/ ; "zeus-darwin-amd64"
|
5
|
+
when /^Linux.*64/ ; "zeus-linux-amd64"
|
6
|
+
when /^Linux.*/ ; "zeus-linux-386"
|
7
|
+
else
|
8
|
+
puts "Zeus is not supported on your platform."
|
9
|
+
puts "It's not likely to ever be possible on Windows."
|
10
|
+
puts "If you're using another platform that you think should work easily, open an issue at:"
|
11
|
+
puts "https://github.com/burke/zeus/issues"
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
zeusgemdir = File.expand_path("../../", __FILE__)
|
16
|
+
exec "#{zeusgemdir}/build/#{exe}", *ARGV
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
if /linux/ =~ RUBY_PLATFORM
|
2
|
+
open("Makefile", "wb") do |f|
|
3
|
+
f.write <<-EOF
|
4
|
+
CXX = g++
|
5
|
+
CXXFLAGS = -O3 -g -Wall
|
6
|
+
|
7
|
+
inotify-wrapper: inotify-wrapper.o
|
8
|
+
$(CXX) $(CXXFLAGS) $< -o $@
|
9
|
+
|
10
|
+
%.o: %.cpp
|
11
|
+
$(CXX) $(CXXFLAGS) -c $< -o $@
|
12
|
+
|
13
|
+
install:
|
14
|
+
# do nothing
|
15
|
+
EOF
|
16
|
+
end
|
17
|
+
else
|
18
|
+
open("Makefile", "wb") do |f|
|
19
|
+
f.write <<-EOF
|
20
|
+
install:
|
21
|
+
# do nothing
|
22
|
+
EOF
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#include <map>
|
2
|
+
#include <string>
|
3
|
+
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
#include <string.h>
|
7
|
+
#include <errno.h>
|
8
|
+
#include <unistd.h>
|
9
|
+
#include <sys/types.h>
|
10
|
+
#include <sys/inotify.h>
|
11
|
+
|
12
|
+
#include <errno.h>
|
13
|
+
|
14
|
+
#define EVENT_SIZE (sizeof (struct inotify_event))
|
15
|
+
#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
|
16
|
+
|
17
|
+
using namespace std;
|
18
|
+
|
19
|
+
static int _inotify_fd;
|
20
|
+
static map<int, string> _WatchedFiles;
|
21
|
+
static map<string, bool> _FileIsWatched;
|
22
|
+
|
23
|
+
// static int inotifyFlags = IN_ATTRIB | IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF;
|
24
|
+
static int inotifyFlags = IN_ATTRIB | IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF;
|
25
|
+
|
26
|
+
void maybeAddFileToWatchList(string file)
|
27
|
+
{
|
28
|
+
if (_FileIsWatched[file]) return;
|
29
|
+
|
30
|
+
int wd = inotify_add_watch(_inotify_fd, file.c_str(), inotifyFlags);
|
31
|
+
int attempts = 0;
|
32
|
+
// Files are momentarily inaccessible when they are rewritten. I couldn't
|
33
|
+
// find a good way to deal with this, so we poll 'deleted' files for 0.25s or so
|
34
|
+
// to see if they reappear.
|
35
|
+
while (wd == -1 && errno == ENOENT) {
|
36
|
+
usleep(10000);
|
37
|
+
wd = inotify_add_watch(_inotify_fd, file.c_str(), inotifyFlags);
|
38
|
+
if (attempts++ == 25) break; // try for at most about a quarter of a second
|
39
|
+
}
|
40
|
+
if (wd != -1) {
|
41
|
+
_WatchedFiles[wd] = file;
|
42
|
+
_FileIsWatched[file] = true;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
// This essentially removes a file from the watchlist then
|
47
|
+
// immediately re-adds it. This is because when a file is rewritten,
|
48
|
+
// as so many editors love to do, the watchdescriptor no longer refers to
|
49
|
+
// the file, so re must re-watch the path.
|
50
|
+
void replaceFileInWatchList(int wd, string file)
|
51
|
+
{
|
52
|
+
_FileIsWatched.erase(file);
|
53
|
+
_WatchedFiles.erase(wd);
|
54
|
+
inotify_rm_watch(_inotify_fd, wd);
|
55
|
+
maybeAddFileToWatchList(file);
|
56
|
+
}
|
57
|
+
|
58
|
+
void handleStdin()
|
59
|
+
{
|
60
|
+
char line[2048];
|
61
|
+
if (fgets(line, sizeof(line), stdin) == NULL) return;
|
62
|
+
line[strlen(line)-1] = 0;
|
63
|
+
|
64
|
+
maybeAddFileToWatchList(string(line));
|
65
|
+
}
|
66
|
+
|
67
|
+
void handleInotify()
|
68
|
+
{
|
69
|
+
int length;
|
70
|
+
int i = 0;
|
71
|
+
char buffer[EVENT_BUF_LEN];
|
72
|
+
string filename;
|
73
|
+
|
74
|
+
length = read(_inotify_fd, buffer, EVENT_BUF_LEN);
|
75
|
+
if (length < 0) return;
|
76
|
+
|
77
|
+
while (i < length) {
|
78
|
+
struct inotify_event *event = (struct inotify_event *) &buffer[i];
|
79
|
+
string file = _WatchedFiles[event->wd];
|
80
|
+
if (file != "") {
|
81
|
+
printf("%s\n", file.c_str());
|
82
|
+
fflush(stdout);
|
83
|
+
replaceFileInWatchList(event->wd, file);
|
84
|
+
}
|
85
|
+
|
86
|
+
i += EVENT_SIZE + event->len;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
void go()
|
91
|
+
{
|
92
|
+
fd_set rfds;
|
93
|
+
int retval;
|
94
|
+
|
95
|
+
for (;;) {
|
96
|
+
FD_ZERO(&rfds);
|
97
|
+
FD_SET(0, &rfds);
|
98
|
+
FD_SET(_inotify_fd, &rfds);
|
99
|
+
|
100
|
+
retval = select(_inotify_fd+1, &rfds, NULL, NULL, NULL);
|
101
|
+
|
102
|
+
if (retval == -1) {
|
103
|
+
// perror("select");
|
104
|
+
} else if (retval) {
|
105
|
+
if (FD_ISSET(0, &rfds)) handleStdin();
|
106
|
+
if (FD_ISSET(_inotify_fd, &rfds)) handleInotify();
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
|
112
|
+
int main(int argc, const char *argv[])
|
113
|
+
{
|
114
|
+
_inotify_fd = inotify_init();
|
115
|
+
go();
|
116
|
+
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Zeus
|
2
|
+
class LoadTracking
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def features_loaded_by(&block)
|
6
|
+
old_features = all_features()
|
7
|
+
yield
|
8
|
+
new_features = all_features() - old_features
|
9
|
+
return new_features
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_feature(file)
|
13
|
+
path = if File.exist?(File.expand_path(file))
|
14
|
+
File.expand_path(file)
|
15
|
+
else
|
16
|
+
find_in_load_path(file)
|
17
|
+
end
|
18
|
+
add_extra_feature(path) if path
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def all_features
|
24
|
+
untracked = defined?($untracked_features) ? $untracked_features : []
|
25
|
+
$LOADED_FEATURES + untracked
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_extra_feature(path)
|
29
|
+
$untracked_features ||= []
|
30
|
+
$untracked_features << path
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_in_load_path(file)
|
34
|
+
$LOAD_PATH.map { |path| "#{path}/#{file}" }.detect{ |file| File.exist? file }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module Kernel
|
41
|
+
|
42
|
+
def load(file, *a)
|
43
|
+
Kernel.load(file, *a)
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
alias_method :__load_without_zeus, :load
|
48
|
+
def load(file, *a)
|
49
|
+
Zeus::LoadTracking.add_feature(file)
|
50
|
+
__load_without_zeus(file, *a)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Zeus
|
4
|
+
module M
|
5
|
+
### Custom wrapper around an array of test methods
|
6
|
+
# In charge of some smart querying, filtering, sorting, etc on the the
|
7
|
+
# test methods
|
8
|
+
class TestCollection
|
9
|
+
include Enumerable
|
10
|
+
extend Forwardable
|
11
|
+
# This should act like an array, so forward some common methods over to the
|
12
|
+
# internal collection
|
13
|
+
def_delegators :@collection, :size, :<<, :each, :empty?
|
14
|
+
|
15
|
+
def initialize(collection = nil)
|
16
|
+
@collection = collection || []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Slice out tests that may be within the given line.
|
20
|
+
# Returns a new TestCollection with the results.
|
21
|
+
def within(line)
|
22
|
+
# Into a new collection, filter only the tests that...
|
23
|
+
self.class.new(select do |test|
|
24
|
+
# are within the given boundary for this method
|
25
|
+
# or include everything if the line given is nil (no line)
|
26
|
+
line.nil? || (test.start_line..test.end_line).include?(line)
|
27
|
+
end)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Used to line up method names in `#sprintf` when `m` aborts
|
31
|
+
def column_size
|
32
|
+
# Boil down the collection of test methods to the name of the method's
|
33
|
+
# size, then find the largest one
|
34
|
+
@column_size ||= map { |test| test.name.to_s.size }.max
|
35
|
+
end
|
36
|
+
|
37
|
+
# Be considerate when printing out tests and pre-sort them by line number
|
38
|
+
def by_line_number(&block)
|
39
|
+
# On each member of the collection, sort by line number and yield
|
40
|
+
# the block into the sorted collection
|
41
|
+
sort_by(&:start_line).each(&block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def contains? test_name
|
45
|
+
@collection.each do |test|
|
46
|
+
return true if test_name.match(test.name)
|
47
|
+
end
|
48
|
+
false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Zeus
|
2
|
+
module M
|
3
|
+
### Simple data structure for what a test method contains.
|
4
|
+
#
|
5
|
+
# Too lazy to make a class for this when it's really just a bag of data
|
6
|
+
# without any behavior.
|
7
|
+
#
|
8
|
+
# Includes the name of this method, what line on the file it begins on,
|
9
|
+
# and where it ends.
|
10
|
+
class TestMethod < Struct.new(:name, :start_line, :end_line)
|
11
|
+
# Set up a new test method for this test suite class
|
12
|
+
def self.create(suite_class, test_method, find_locations = true)
|
13
|
+
# Hopefully it's been defined as an instance method, so we'll need to
|
14
|
+
# look up the ruby Method instance for it
|
15
|
+
method = suite_class.instance_method(test_method)
|
16
|
+
|
17
|
+
if find_locations
|
18
|
+
# Ruby can find the starting line for us, so pull that out of the array
|
19
|
+
start_line = method.source_location.last
|
20
|
+
|
21
|
+
# Ruby can't find the end line however, and I'm too lazy to write
|
22
|
+
# a parser. Instead, `method_source` adds `Method#source` so we can
|
23
|
+
# deduce this ourselves.
|
24
|
+
#
|
25
|
+
# The end line should be the number of line breaks in the method source,
|
26
|
+
# added to the starting line and subtracted by one.
|
27
|
+
end_line = method.source.split("\n").size + start_line - 1
|
28
|
+
end
|
29
|
+
|
30
|
+
# Shove the given attributes into a new databag
|
31
|
+
new(test_method, start_line, end_line)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|