polyamory 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.md +35 -0
  2. data/bin/polyamory +24 -0
  3. data/lib/polyamory.rb +204 -0
  4. metadata +66 -0
@@ -0,0 +1,35 @@
1
+ # Polyamory
2
+
3
+ Polyamory loves *all* of your testing frameworks. It is a command-line tool that is able to run your test files regardless of the framework being used.
4
+
5
+ Features:
6
+
7
+ * easily run the full test suite for any project: just type `polyamory`
8
+ * use a directory name on the command line to run everything inside that directory
9
+ * use a keyword to run all test files which contain that word
10
+ * Bundler support
11
+
12
+ Frameworks supported:
13
+
14
+ * Cucumber in `"features/**/*.feature"`
15
+ * RSpec + Shoulda in `"spec/**/*_spec.rb"`
16
+ * test/unit, Shoulda, or anything else in `"test/**/*_test.rb"`
17
+
18
+ ## Examples
19
+
20
+ Here, `polyamory` is aliased as `pam` for brevity.
21
+
22
+ # run everything
23
+ $ pam
24
+ > rspec spec &&
25
+ cucumber -f progress -t ~@wip features &&
26
+ polyamory -t test
27
+
28
+ # everyting inside a single directory
29
+ $ pam test/unit
30
+ > polyamory -t test/unit
31
+
32
+ # run test files matching keyword
33
+ $ pam user
34
+ > polyamory -t spec/models/user_spec.rb spec/controllers/user_controller.rb &&
35
+ cucumber -f progress -t ~@wip features/user_registration.feature
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ if ARGV.delete '-t'
3
+ test_files = ARGV.map { |arg|
4
+ unless arg.index('-') == 0
5
+ if File.directory? arg
6
+ Dir["#{arg}/**/*_test.rb"]
7
+ else
8
+ arg
9
+ end
10
+ end
11
+ }.compact.flatten
12
+
13
+ if test_files.empty?
14
+ warn "polyamory: nothing to load"
15
+ exit 1
16
+ else
17
+ test_files.each { |f| load f }
18
+ end
19
+ else
20
+ require 'polyamory'
21
+ options = {}
22
+ options[:noop] = !!ARGV.delete('-n')
23
+ Polyamory.run ARGV, Dir.pwd, options
24
+ end
@@ -0,0 +1,204 @@
1
+ require 'pathname'
2
+
3
+ class Polyamory
4
+ def self.run(*args)
5
+ new(*args).run
6
+ end
7
+
8
+ def initialize(names, root, options = {})
9
+ @names = names
10
+ @root = Pathname.new(root).expand_path
11
+ @options = options
12
+ end
13
+
14
+ def noop?
15
+ @options[:noop]
16
+ end
17
+
18
+ def file_exists?(path)
19
+ (@root + path).exist?
20
+ end
21
+
22
+ def bundler?
23
+ file_exists? 'Gemfile'
24
+ end
25
+
26
+ def test_dir
27
+ @root + 'test'
28
+ end
29
+
30
+ def test_glob(dir = test_dir)
31
+ "#{dir}/**/*_test.rb"
32
+ end
33
+
34
+ def spec_dir
35
+ @root + 'spec'
36
+ end
37
+
38
+ def spec_glob(dir = spec_dir)
39
+ "#{dir}/**/*_spec.rb"
40
+ end
41
+
42
+ def features_dir
43
+ @root + 'features'
44
+ end
45
+
46
+ def features_glob(dir = features_dir)
47
+ "#{dir}/**/*.feature"
48
+ end
49
+
50
+ class Pathname < Pathname
51
+ attr_reader :root
52
+
53
+ def self.glob(patterns, root)
54
+ patterns = Array(patterns)
55
+ Dir[*patterns].map do |path|
56
+ self.new(path, root)
57
+ end
58
+ end
59
+
60
+ def initialize(path, root_path = nil)
61
+ super(path)
62
+ self.root = root_path
63
+ end
64
+
65
+ def root=(path)
66
+ @relativized = nil
67
+ @root = path
68
+ end
69
+
70
+ def relative
71
+ @relativized ||= relative_path_from root
72
+ end
73
+
74
+ def =~(pattern)
75
+ relative.to_s =~ pattern
76
+ end
77
+
78
+ def +(other)
79
+ result = self.class.new(plus(@path, other.to_s))
80
+ result.root ||= self
81
+ result
82
+ end
83
+ end
84
+
85
+ def find_files
86
+ all_paths = Pathname.glob([test_glob, spec_glob, features_glob], @root)
87
+
88
+ if @names.any?
89
+ @names.map { |name|
90
+ path = @root + name
91
+ pattern = /\b#{Regexp.escape name}(\b|_)/
92
+
93
+ if path.directory? or not path.extname.empty?
94
+ path
95
+ else
96
+ all_paths.select { |p| p =~ pattern }
97
+ end
98
+ }.flatten
99
+ else
100
+ [test_dir, spec_dir, features_dir].select { |p| p.directory? }
101
+ end
102
+ end
103
+
104
+ def relativize(paths)
105
+ Array(paths).map { |p| p.relative }
106
+ end
107
+
108
+ def run
109
+ paths = relativize(find_files)
110
+ if paths.empty?
111
+ warn "nothing found to run"
112
+ exit 1
113
+ end
114
+
115
+ jobs = index_by_path_prefix(paths).map do |prefix, files|
116
+ [runner_for_prefix(prefix), *files].flatten
117
+ end
118
+
119
+ prepare_env
120
+ execute_jobs jobs
121
+ end
122
+
123
+ def runner_for_prefix(prefix)
124
+ case prefix
125
+ when 'features' then %w[cucumber -f progress -t ~@wip]
126
+ when 'spec' then detect_rspec_version
127
+ when 'test' then %w[polyamory -t]
128
+ else
129
+ raise ArgumentError, "don't know a runner for #{prefix}"
130
+ end
131
+ end
132
+
133
+ def detect_rspec_version
134
+ helper = 'spec/spec_helper.rb'
135
+
136
+ if file_exists? 'spec/spec.opts' or file_exists? 'lib/tasks/rspec.rake'
137
+ 'spec'
138
+ elsif file_exists? '.rspec'
139
+ 'rspec'
140
+ elsif helper.exist?
141
+ File.open(helper) do |file|
142
+ while file.gets
143
+ return $&.downcase if $_ =~ /\bR?Spec\b/
144
+ end
145
+ end
146
+ 'rspec'
147
+ else
148
+ 'rspec'
149
+ end
150
+ end
151
+
152
+ def index_by_path_prefix(paths)
153
+ paths.inject(Hash.new {|h,k| h[k] = [] }) do |index, path|
154
+ prefix = path.to_s.split('/', 2).first
155
+ index[prefix] << path
156
+ index
157
+ end
158
+ end
159
+
160
+ def execute_jobs(jobs)
161
+ if jobs.size > 1
162
+ jobs.each { |j| cmd(j, true) }
163
+ else
164
+ cmd(jobs.first)
165
+ end
166
+ end
167
+
168
+ def prepare_cmdline(args)
169
+ args = args.map { |p| p.to_s }
170
+ args = %w[bundle exec] + args if args.first != 'polyamory' and bundler?
171
+ args
172
+ end
173
+
174
+ def cmd(args, many = false)
175
+ args = prepare_cmdline(args)
176
+ puts args.join(' ')
177
+
178
+ # TODO: hack; make this configurable, use bundler
179
+ with_rubyopt(args.first == 'polyamory' ? '-rubygems' : nil) do
180
+ if many
181
+ system(*args)
182
+ exit $?.exitstatus unless $?.success?
183
+ else
184
+ exec(*args)
185
+ end
186
+ end unless noop?
187
+ end
188
+
189
+ def with_rubyopt(value)
190
+ old_value = ENV['RUBYOPT']
191
+ ENV['RUBYOPT'] = "#{value} #{old_value}"
192
+
193
+ begin
194
+ yield
195
+ ensure
196
+ ENV['RUBYOPT'] = old_value
197
+ end
198
+ end
199
+
200
+ def prepare_env
201
+ # TODO: make this per-job
202
+ ENV['RUBYOPT'] = ENV['RUBYOPT'].gsub(/(^| )-w( |$)/, '\1\2') if ENV['RUBYOPT']
203
+ end
204
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: polyamory
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 2
9
+ version: 0.0.2
10
+ platform: ruby
11
+ authors:
12
+ - "Mislav Marohni\xC4\x87"
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-12-30 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: A tool that knows how to run your tests regardless of framework
22
+ email: mislav.marohnic@gmail.com
23
+ executables:
24
+ - polyamory
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - bin/polyamory
31
+ - lib/polyamory.rb
32
+ - README.md
33
+ has_rdoc: true
34
+ homepage: http://github.com/mislav/polyamory
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ segments:
48
+ - 0
49
+ version: "0"
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.7
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Runs your tests
65
+ test_files: []
66
+