lib/polytrix/command.rb in polytrix-0.1.2 vs lib/polytrix/command.rb in polytrix-0.1.3

- old
+ new

@@ -1,17 +1,14 @@ require 'thread' module Polytrix module Command - class Base + class Base # rubocop:disable ClassLength include Polytrix::DefaultLogger include Polytrix::Logging - include Polytrix::Core::FileSystemHelper + include Polytrix::Util::FileSystem - # Need standard executor... - SUPPORTED_EXTENSIONS = %w(py rb js) - # Contstructs a new Command object. # # @param cmd_args [Array] remainder of the arguments from processed ARGV # @param cmd_options [Hash] hash of Thor options # @param options [Hash] configuration options @@ -26,13 +23,13 @@ @args = cmd_args @options = cmd_options @action = options.fetch(:action, nil) @help = options.fetch(:help, -> { 'No help provided' }) @manifest_file = options.fetch('manifest', nil) - @test_dir = options.fetch('test_dir', nil) @loader = options.fetch(:loader, nil) @shell = options.fetch(:shell) + @queue = Queue.new end private # @return [Array] remainder of the arguments from processed ARGV @@ -45,69 +42,22 @@ # @return [proc] a callable that displays help for the command # @api private attr_reader :help - # @return [Config] a Config object - # @api private - attr_reader :test_dir - # @return [Thor::Shell] a Thor shell object # @api private attr_reader :shell # @return [String] the action to perform # @api private attr_reader :action def setup - manifest_file = File.expand_path @manifest_file - if File.exists? manifest_file - logger.debug "Loading manifest file: #{manifest_file}" - Polytrix.configuration.manifest = @manifest_file - elsif @options.solo - solo_setup - else - fail StandardError, "No manifest found at #{manifest_file} and not using --solo mode" - end - - Polytrix.configuration.documentation_dir = options[:target_dir] - Polytrix.configuration.documentation_format = options[:format] - - manifest.build_challenges - - test_dir = @test_dir.nil? ? nil : File.expand_path(@test_dir) - if test_dir && File.directory?(test_dir) - $LOAD_PATH.unshift test_dir - Dir["#{test_dir}/**/*.rb"].each do | file_to_require | - require relativize(file_to_require, test_dir).to_s.gsub('.rb', '') - end - end + Polytrix.setup(options, @manifest_file) end - def solo_setup - suites = {} - solo_basedir = @options.solo - solo_glob = @options.fetch('solo_glob', "**/*.{#{SUPPORTED_EXTENSIONS.join(',')}}") - Dir[File.join(solo_basedir, solo_glob)].sort.each do | code_sample | - code_sample = Pathname.new(code_sample) - suite_name = relativize(code_sample.dirname, solo_basedir).to_s - suite_name = solo_basedir if suite_name == '.' - scenario_name = code_sample.basename(code_sample.extname).to_s - suite = suites[suite_name] ||= Polytrix::Manifest::Suite.new(samples: []) - suite.samples << scenario_name - end - @manifest = Polytrix.configuration.manifest = Polytrix::Manifest.new( - implementors: { - File.basename(solo_basedir) => { - basedir: solo_basedir - } - }, - suites: suites - ) - end - def manifest @manifest ||= Polytrix.configuration.manifest @manifest end @@ -122,58 +72,25 @@ logger.error "\n#{msg}\n\n" help.call exit 1 end - # @return [Array<Scenario>] an array of scenarios - # @raise [SystemExit] if no scenario are returned - # @api private - def all_scenarios - result = manifest.challenges.values - - if result.empty? - die 'No scenarios defined' - else - result - end - end - - # Return an array on scenarios whos name matches the regular expression. - # - # @param regexp [Regexp] a regular expression matching on instance names - # @return [Array<Instance>] an array of scenarios - # @raise [SystemExit] if no scenarios are returned or the regular - # expression is invalid - # @api private - def filtered_scenarios(regexp) - result = begin - manifest.challenges.get(regexp) || - manifest.challenges.get_all(/#{regexp}/) - rescue RegexpError => e - die "Invalid Ruby regular expression, " \ - "you may need to single quote the argument. " \ - "Please try again or consult http://rubular.com/ (#{e.message})" - end - result = [result] unless result.is_a? Array - - if result.empty? - die "No scenarios for regex `#{regexp}', try running `polytrix list'" - else - result - end - end - # Return an array on scenarios whos name matches the regular expression, # the full instance name, or the `"all"` literal. # # @param arg [String] an instance name, a regular expression, the literal # `"all"`, or `nil` # @return [Array<Instance>] an array of scenarios # @api private - def parse_subcommand(arg = nil) - arg ||= 'all' - arg == 'all' ? all_scenarios : filtered_scenarios(arg) + def parse_subcommand(regexp = nil) + scenarios = Polytrix.filter_scenarios(regexp, options) + die "No scenarios for regex `#{regexp}', try running `polytrix list'" if scenarios.empty? + scenarios + rescue RegexpError => e + die 'Invalid Ruby regular expression, ' \ + 'you may need to single quote the argument. ' \ + "Please try again or consult http://rubular.com/ (#{e.message})" end end # Common module to execute a Polytrix action such as create, converge, etc. module RunAction @@ -182,29 +99,45 @@ # seperate thread of execution which may or may not be running # concurrently. # # @param action [String] action to perform # @param scenarios [Array<Instance>] an array of scenarios - def run_action(action, scenarios, *args) + def run_action(_action, scenarios, *_args) concurrency = 1 if options[:concurrency] concurrency = options[:concurrency] || scenarios.size concurrency = scenarios.size if concurrency > scenarios.size end - queue = Queue.new - scenarios.each { |i| queue << i } - concurrency.times { queue << nil } + scenarios.each { |i| @queue << i } + concurrency.times { @queue << nil } - threads = [] - concurrency.times do - threads << Thread.new do - while (instance = queue.pop) + threads = concurrency.times.map { |i| spawn(i) } + threads.map do |t| + begin + t.join + rescue Polytrix::ExecutionError, Polytrix::ChallengeFailure + # respawn thread + t.kill + threads.delete(t) + threads.push(spawn) + end + end while threads.any?(&:alive?) + end + + private + + def spawn(i) + Thread.new(i) do |test_env_number| + Thread.current[:test_env_number] = test_env_number + while (instance = @queue.pop) + begin instance.public_send(action, *args) + rescue Polytrix::ExecutionError, Polytrix::ChallengeFailure => e + logger.error(e) end end end - threads.map { |i| i.join } end end end end