Class: PRSpec
- Inherits:
-
Object
- Object
- PRSpec
- Defined in:
- lib/prspec.rb
Constant Summary collapse
- SPEC_FILE_FILTER =
'_spec.rb'
- INFO_FILE =
".prspec"
Instance Attribute Summary collapse
-
#num_threads ⇒ Object
Returns the value of attribute num_threads.
-
#output ⇒ Object
Returns the value of attribute output.
-
#processes ⇒ Object
Returns the value of attribute processes.
-
#tests ⇒ Object
Returns the value of attribute tests.
Class Method Summary collapse
Instance Method Summary collapse
- #begin_run(processes, options) ⇒ Object
- #build_process_array(process_tests, opts) ⇒ Object
- #close ⇒ Object
- #divide_spec_tests(tests) ⇒ Object
- #get_number_of_processors ⇒ Object
- #get_spec_files(options) ⇒ Object
- #get_spec_tests(options) ⇒ Object
- #get_test_description(description_line) ⇒ Object
- #get_tests_from_files(files, options) ⇒ Object
-
#initialize(args) ⇒ PRSpec
constructor
A new instance of PRSpec.
- #parse_args(args) ⇒ Object
- #running? ⇒ Boolean
- #update_running_thread_count(count) ⇒ Object
Constructor Details
#initialize(args) ⇒ PRSpec
Returns a new instance of PRSpec.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/prspec.rb', line 31 def initialize(args) @output = '' # create tracking file yml = { :running_threads => 0 } File.open(INFO_FILE, 'w') { |f| f.write yml.to_yaml } if (!args.nil? && args.length > 0 && !args[0].nil?) opts = parse_args(args) if (!opts[:help]) @num_threads = opts[:thread_count] @tests = get_spec_tests(opts) if (tests.length > 0) process_tests = divide_spec_tests(tests) $log.debug "#{tests.length} Spec tests divided among #{@num_threads} arrays." else $log.warn "No spec tests found. Exiting." exit 1 end $log.info "Creating array of Child Processes..." @processes = build_process_array(process_tests, opts) begin_run(@processes, opts) end end ensure FileUtils.remove_file(INFO_FILE, :force => true) if File.exists?(INFO_FILE) end |
Instance Attribute Details
#num_threads ⇒ Object
Returns the value of attribute num_threads.
27 28 29 |
# File 'lib/prspec.rb', line 27 def num_threads @num_threads end |
#output ⇒ Object
Returns the value of attribute output.
27 28 29 |
# File 'lib/prspec.rb', line 27 def output @output end |
#processes ⇒ Object
Returns the value of attribute processes.
27 28 29 |
# File 'lib/prspec.rb', line 27 def processes @processes end |
#tests ⇒ Object
Returns the value of attribute tests.
27 28 29 |
# File 'lib/prspec.rb', line 27 def tests @tests end |
Class Method Details
.get_number_of_running_threads ⇒ Object
149 150 151 152 |
# File 'lib/prspec.rb', line 149 def self.get_number_of_running_threads prspec_info = YAML.load_file(INFO_FILE) return prspec_info[:running_threads].to_i end |
.is_windows? ⇒ Boolean
319 320 321 |
# File 'lib/prspec.rb', line 319 def self.is_windows? return (RUBY_PLATFORM.match(/mingw/i)) ? true : false end |
Instance Method Details
#begin_run(processes, options) ⇒ Object
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/prspec.rb', line 257 def begin_run(processes, ) if (!processes.nil? && processes.length > 0) $log.info "Starting all Child Processes..." update_running_thread_count(processes.length) processes.each do |proc| if (proc.is_a?(PRSpecThread) && .is_a?(Hash)) proc.start unless [:test_mode] else raise "Invalid datatype where PRSpecThread or Hash exepcted. Found: #{proc.class.to_s}, #{.class.to_s}" end end $log.info "All processes started..." while processes.length > 0 processes.each do |proc| if (!proc.done?) # confirm threads are running $log.debug "Thread#{proc.id}: alive..." else $log.debug "Thread#{proc.id}: done." if ([:serialize]) puts proc.output unless [:quiet_mode] end # collect thread output if in quiet mode if ([:quiet_mode]) @output << proc.output end processes.delete(proc) # remove from the array of processes so we don't count it again update_running_thread_count(processes.length) end end sleep 0.5 # wait half a second for processes to run and then re-check their status end $log.info "All processes complete." else raise "Invalid input passed to method: 'processes' must be a valid Array of PRSpecThread objects" end end |
#build_process_array(process_tests, opts) ⇒ Object
249 250 251 252 253 254 255 |
# File 'lib/prspec.rb', line 249 def build_process_array(process_tests, opts) processes = [] for i in 0..@num_threads-1 processes[i] = PRSpecThread.new(i, process_tests[i], {'TEST_ENV_NUMBER'=>i, 'HOME'=>nil}, opts) end return processes end |
#close ⇒ Object
313 314 315 316 317 |
# File 'lib/prspec.rb', line 313 def close @processes.each do |proc| proc.close end end |
#divide_spec_tests(tests) ⇒ Object
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/prspec.rb', line 223 def divide_spec_tests(tests) if (tests.length < @num_threads) @num_threads = tests.length $log.info "reducing number of threads due to low number of spec tests found: Threads = #{@num_threads}" end spec_arrays = Array.new(@num_threads) num_per_thread = tests.length.fdiv(@num_threads).ceil $log.debug "Approximate number of tests per thread: #{num_per_thread}" # ensure an even distribution i = 0 tests.each do |tname| if (i >= @num_threads) i = 0 end if (spec_arrays[i].nil?) spec_arrays[i] = [] end spec_arrays[i].push(tname) i+=1 end return spec_arrays end |
#get_number_of_processors ⇒ Object
144 145 146 147 |
# File 'lib/prspec.rb', line 144 def get_number_of_processors count = Parallel.processor_count return count end |
#get_spec_files(options) ⇒ Object
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/prspec.rb', line 161 def get_spec_files() base_dir = [:dir] path = [:path] if (path.nil? || path == '') path = '.' end full_path = "" if (path.end_with?('.rb')) full_path = File.join(base_dir, path.to_s) else full_path = File.join(base_dir, path.to_s, '**', "*#{SPEC_FILE_FILTER}") end $log.debug "full_path: #{full_path}" files = [] if ([:excludes].nil?) files = Dir.glob(full_path) else files = Dir.glob(full_path).reject { |f| f[[:excludes]] } end return files end |
#get_spec_tests(options) ⇒ Object
154 155 156 157 158 159 |
# File 'lib/prspec.rb', line 154 def get_spec_tests() files = get_spec_files() tests = get_tests_from_files(files, ) $log.debug "Found #{tests.length} tests in #{files.length} files" return tests end |
#get_test_description(description_line) ⇒ Object
216 217 218 219 220 221 |
# File 'lib/prspec.rb', line 216 def get_test_description(description_line) # get index of first "'" trim_front = description_line.sub(/[\s]*(it)[\s]*(')/,'') description = trim_front.sub(/(')[\s]*(,[\s\S]*)*(do|{)[\s\S]*/,'') return description.gsub(/["]/,'\"').gsub(/\\'/,"'") end |
#get_tests_from_files(files, options) ⇒ Object
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/prspec.rb', line 185 def get_tests_from_files(files, ) tests = [] match_test_name_format = /^[\s]*(it)[\s]*(')[\s\S]*(')[\s\S]*(do|{)/ files.each do |file| lines = File.readlines(file) for i in 0..lines.length-1 if lines[i] =~ match_test_name_format m = lines[i] match = true if ([:tag_name] != '') if (m.rindex([:tag_name]).nil? || (m.rindex([:tag_value]) <= m.rindex([:tag_name]))) match = false end end # if ignore_pending specified then skip tests containing 'pending' on next line if ([:ignore_pending]) if (i+1 < lines.length-1 && lines[i+1].include?('pending')) match = false end end if (match) description = get_test_description(m) tests.push("\"#{description}\"") end end end end return tests end |
#parse_args(args) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/prspec.rb', line 60 def parse_args(args) $log.debug("Parsing arguments of: #{args}") = { :dir=>'.', :path=>'spec', :thread_count=>get_number_of_processors, :test_mode=>false, :help=>false, :excludes=>nil, :rspec_args=>[], :tag_name=>'', :tag_value=>'', :ignore_pending=>false, :serialize=>false } o = OptionParser.new do |opts| opts. = "Usage: prspec [options]" opts.on("-p", "--path PATH", "Relative path from the base directory to search for spec files") do |v| $log.debug "path specified... value: #{v}" [:path] = v end opts.on("-e", "--exclude REGEX", "Regex string used to exclude files") do |v| $log.debug "excludes specified... value: #{v}" [:excludes] = v end opts.on("-d", "--dir DIRECTORY", "The base directory to run from") do |v| $log.debug "directory specified... value: #{v}" [:dir] = v end opts.on("-n", "--num-threads THREADS", "The number of threads to use") do |v| $log.debug "number of threads specified... value: #{v}" [:thread_count] = v.to_i end opts.on("-t", "--tag TAG", "A rspec tag value to filter by") do |v| $log.debug "tag filter specified... value: #{v}" tag = v value = 'true' if (v.include?(':')) # split to tag and value tag_value = v.split(':') tag = ":#{tag_value[0]}" value = "#{tag_value[1]}" end [:tag_name] = tag [:tag_value] = value end opts.on("-r", "--rspec-args \"RSPEC_ARGS\"", "Additional arguments to be passed to rspec (must be surrounded with double quotes)") do |v| $log.debug "rspec arguments specified... value: #{v}" [:rspec_args] = v.gsub(/"/,'').split(' ') # create an array of each argument end opts.on("--test-mode", "Do everything except actually starting the test threads") do $log.debug "test mode specified... threads will NOT be started." [:test_mode] = true end opts.on("-q", "--quiet", "Quiet mode. Do not display parallel thread output") do $log.debug "quiet mode specified... thread output will not be displayed" [:quiet_mode] = true end opts.on("-h", "--help", "Display a help message") do $log.debug "help message requested..." [:help] = true puts opts end opts.on("--ignore-pending", "Ignore all pending tests") do $log.debug "ignore pending specified... all pending tests will be excluded" [:ignore_pending] = true end opts.on("-s", "--serialize-output", "Wait for each thread to complete and then output to STDOUT serially") do $log.debug "serialize output specified... all threads will output to STDOUT as they complete" [:serialize] = true end end # handle invalid options begin o.parse! args rescue OptionParser::InvalidOption => e $log.error e puts o exit 1 end return end |
#running? ⇒ Boolean
303 304 305 306 307 308 309 310 311 |
# File 'lib/prspec.rb', line 303 def running? @processes.each do |proc| if (!proc.done?) $log.debug "Found running process..." return true end end return false end |
#update_running_thread_count(count) ⇒ Object
294 295 296 297 298 299 300 301 |
# File 'lib/prspec.rb', line 294 def update_running_thread_count(count) (file = File.new(INFO_FILE,'w')).flock(File::LOCK_EX) yml = { :running_threads => count } file.write yml.to_yaml ensure file.flock(File::LOCK_UN) file.close end |