diff options
Diffstat (limited to 'cucumber/features/support/helpers/misc_helpers.rb')
-rw-r--r-- | cucumber/features/support/helpers/misc_helpers.rb | 344 |
1 files changed, 0 insertions, 344 deletions
diff --git a/cucumber/features/support/helpers/misc_helpers.rb b/cucumber/features/support/helpers/misc_helpers.rb deleted file mode 100644 index 865d2978..00000000 --- a/cucumber/features/support/helpers/misc_helpers.rb +++ /dev/null @@ -1,344 +0,0 @@ -require 'date' -require 'io/console' -require 'pry' -require 'timeout' -require 'test/unit' - -# Test::Unit adds an at_exit hook which, among other things, consumes -# the command-line arguments that were intended for cucumber. If -# e.g. `--format` was passed it will throw an error since it's not a -# valid option for Test::Unit, and it throwing an error at this time -# (at_exit) will make Cucumber think it failed and consequently exit -# with an error. Fooling Test::Unit that this hook has already run -# works around this craziness. -Test::Unit.run = true - -# Make all the assert_* methods easily accessible in any context. -include Test::Unit::Assertions - -def assert_vmcommand_success(p, msg = nil) - assert(p.success?, msg.nil? ? "Command failed: #{p.cmd}\n" + \ - "error code: #{p.returncode}\n" \ - "stderr: #{p.stderr}" : \ - msg) -end - -# It's forbidden to throw this exception (or subclasses) in anything -# but try_for() below. Just don't use it anywhere else! -class UniqueTryForTimeoutError < Exception -end - -# Call block (ignoring any exceptions it may throw) repeatedly with -# one second breaks until it returns true, or until `timeout` seconds have -# passed when we throw a Timeout::Error exception. If `timeout` is `nil`, -# then we just run the code block with no timeout. -def try_for(timeout, options = {}) - if block_given? && timeout.nil? - return yield - end - options[:delay] ||= 1 - last_exception = nil - # Create a unique exception used only for this particular try_for - # call's Timeout to allow nested try_for:s. If we used the same one, - # the innermost try_for would catch all outer ones', creating a - # really strange situation. - unique_timeout_exception = Class.new(UniqueTryForTimeoutError) - Timeout::timeout(timeout, unique_timeout_exception) do - loop do - begin - return if yield - rescue NameError, UniqueTryForTimeoutError => e - # NameError most likely means typos, and hiding that is rarely - # (never?) a good idea, so we rethrow them. See below why we - # also rethrow *all* the unique exceptions. - raise e - rescue Exception => e - # All other exceptions are ignored while trying the - # block. Well we save the last exception so we can print it in - # case of a timeout. - last_exception = e - end - sleep options[:delay] - end - end - # At this point the block above either succeeded and we'll return, - # or we are throwing an exception. If the latter, we either have a - # NameError that we'll not catch (and will any try_for below us in - # the stack), or we have a unique exception. That can mean one of - # two things: - # 1. it's the one unique to this try_for, and in that case we'll - # catch it, rethrowing it as something that will be ignored by - # inside the blocks of all try_for:s below us in the stack. - # 2. it's an exception unique to another try_for. Assuming that we - # do not throw the unique exceptions in any other place or way - # than we do it in this function, this means that there is a - # try_for below us in the stack to which this exception must be - # unique to. - # Let 1 be the base step, and 2 the inductive step, and we sort of - # an inductive proof for the correctness of try_for when it's - # nested. It shows that for an infinite stack of try_for:s, any of - # the unique exceptions will be caught only by the try_for instance - # it is unique to, and all try_for:s in between will ignore it so it - # ends up there immediately. -rescue unique_timeout_exception => e - msg = options[:msg] || 'try_for() timeout expired' - exc_class = options[:exception] || Timeout::Error - if last_exception - msg += "\nLast ignored exception was: " + - "#{last_exception.class}: #{last_exception}" - end - raise exc_class.new(msg) -end - -class TorFailure < StandardError -end - -class MaxRetriesFailure < StandardError -end - -def force_new_tor_circuit() - debug_log("Forcing new Tor circuit...") - # Tor rate limits NEWNYM to at most one per 10 second period. - interval = 10 - if $__last_newnym - elapsed = Time.now - $__last_newnym - # We sleep an extra second to avoid tight timings. - sleep interval - elapsed + 1 if 0 < elapsed && elapsed < interval - end - $vm.execute_successfully('tor_control_send "signal NEWNYM"', :libs => 'tor') - $__last_newnym = Time.now -end - -# This will retry the block up to MAX_NEW_TOR_CIRCUIT_RETRIES -# times. The block must raise an exception for a run to be considered -# as a failure. After a failure recovery_proc will be called (if -# given) and the intention with it is to bring us back to the state -# expected by the block, so it can be retried. -def retry_tor(recovery_proc = nil, &block) - tor_recovery_proc = Proc.new do - force_new_tor_circuit - recovery_proc.call if recovery_proc - end - - retry_action($config['MAX_NEW_TOR_CIRCUIT_RETRIES'], - :recovery_proc => tor_recovery_proc, - :operation_name => 'Tor operation', &block) -end - -def retry_action(max_retries, options = {}, &block) - assert(max_retries.is_a?(Integer), "max_retries must be an integer") - options[:recovery_proc] ||= nil - options[:operation_name] ||= 'Operation' - - retries = 1 - loop do - begin - block.call - return - rescue NameError => e - # NameError most likely means typos, and hiding that is rarely - # (never?) a good idea, so we rethrow them. - raise e - rescue Exception => e - if retries <= max_retries - debug_log("#{options[:operation_name]} failed (Try #{retries} of " + - "#{max_retries}) with:\n" + - "#{e.class}: #{e.message}") - options[:recovery_proc].call if options[:recovery_proc] - retries += 1 - else - raise MaxRetriesFailure.new("#{options[:operation_name]} failed (despite retrying " + - "#{max_retries} times) with\n" + - "#{e.class}: #{e.message}") - end - end - end -end - -alias :retry_times :retry_action - -class TorBootstrapFailure < StandardError -end - -def wait_until_tor_is_working - try_for(270) { $vm.execute('/usr/local/sbin/tor-has-bootstrapped').success? } -rescue Timeout::Error - raise TorBootstrapFailure.new('Tor failed to bootstrap') -end - -def convert_bytes_mod(unit) - case unit - when "bytes", "b" then mod = 1 - when "KB" then mod = 10**3 - when "k", "KiB" then mod = 2**10 - when "MB" then mod = 10**6 - when "M", "MiB" then mod = 2**20 - when "GB" then mod = 10**9 - when "G", "GiB" then mod = 2**30 - when "TB" then mod = 10**12 - when "T", "TiB" then mod = 2**40 - else - raise "invalid memory unit '#{unit}'" - end - return mod -end - -def convert_to_bytes(size, unit) - return (size*convert_bytes_mod(unit)).to_i -end - -def convert_to_MiB(size, unit) - return (size*convert_bytes_mod(unit) / (2**20)).to_i -end - -def convert_from_bytes(size, unit) - return size.to_f/convert_bytes_mod(unit).to_f -end - -def cmd_helper(cmd, env = {}) - if cmd.instance_of?(Array) - cmd << {:err => [:child, :out]} - elsif cmd.instance_of?(String) - cmd += " 2>&1" - end - env = ENV.to_h.merge(env) - IO.popen(env, cmd) do |p| - out = p.readlines.join("\n") - p.close - ret = $? - assert_equal(0, ret, "Command failed (returned #{ret}): #{cmd}:\n#{out}") - return out - end -end - -def all_tor_hosts - nodes = Array.new - chutney_torrcs = Dir.glob( - "#{$config['TMPDIR']}/chutney-data/nodes/*/torrc" - ) - chutney_torrcs.each do |torrc| - open(torrc) do |f| - nodes += f.grep(/^(Or|Dir)Port\b/).map do |line| - { address: $vmnet.bridge_ip_addr, port: line.split.last.to_i } - end - end - end - return nodes -end - -def allowed_hosts_under_tor_enforcement - all_tor_hosts + @lan_hosts -end - -def get_free_space(machine, path) - case machine - when 'host' - assert(File.exists?(path), "Path '#{path}' not found on #{machine}.") - free = cmd_helper(["df", path]) - when 'guest' - assert($vm.file_exist?(path), "Path '#{path}' not found on #{machine}.") - free = $vm.execute_successfully("df '#{path}'") - else - raise 'Unsupported machine type #{machine} passed.' - end - output = free.split("\n").last - return output.match(/[^\s]\s+[0-9]+\s+[0-9]+\s+([0-9]+)\s+.*/)[1].chomp.to_i -end - -def random_string_from_set(set, min_len, max_len) - len = (min_len..max_len).to_a.sample - len ||= min_len - (0..len-1).map { |n| set.sample }.join -end - -def random_alpha_string(min_len, max_len = 0) - alpha_set = ('A'..'Z').to_a + ('a'..'z').to_a - random_string_from_set(alpha_set, min_len, max_len) -end - -def random_alnum_string(min_len, max_len = 0) - alnum_set = ('A'..'Z').to_a + ('a'..'z').to_a + (0..9).to_a.map { |n| n.to_s } - random_string_from_set(alnum_set, min_len, max_len) -end - -# Sanitize the filename from unix-hostile filename characters -def sanitize_filename(filename, options = {}) - options[:replacement] ||= '_' - bad_unix_filename_chars = Regexp.new("[^A-Za-z0-9_\\-.,+:]") - filename.gsub(bad_unix_filename_chars, options[:replacement]) -end - -def info_log_artifact_location(type, path) - if $config['ARTIFACTS_BASE_URI'] - # Remove any trailing slashes, we'll add one ourselves - base_url = $config['ARTIFACTS_BASE_URI'].gsub(/\/*$/, "") - path = "#{base_url}/#{File.basename(path)}" - end - info_log("#{type.capitalize}: #{path}") -end - -def notify_user(message) - alarm_script = $config['NOTIFY_USER_COMMAND'] - return if alarm_script.nil? || alarm_script.empty? - cmd_helper(alarm_script.gsub('%m', message)) -end - -def pause(message = "Paused") - notify_user(message) - STDERR.puts - STDERR.puts message - # Ring the ASCII bell for a helpful notification in most terminal - # emulators. - STDOUT.write "\a" - STDERR.puts - loop do - STDERR.puts "Return: Continue; d: Debugging REPL" - c = STDIN.getch - case c - when "\r" - return - when "d" - binding.pry(quiet: true) - end - end -end - -def dbus_send(service, object_path, method, *args, **opts) - opts ||= {} - ruby_type_to_dbus_type = { - String => 'string', - Fixnum => 'int32', - } - typed_args = args.map do |arg| - type = ruby_type_to_dbus_type[arg.class] - assert_not_nil(type, "No DBus type conversion for Ruby type '#{arg.class}'") - "#{type}:#{arg}" - end - ret = $vm.execute_successfully( - "dbus-send --print-reply --dest=#{service} #{object_path} " + - " #{method} #{typed_args.join(' ')}", - **opts - ).stdout.lines - # The first line written is about timings and other stuff we don't - # care about; we only care about the return values. - ret.shift - ret.map! do |s| - type, val = /^\s*(\S+)\s+(\S+)$/.match(s)[1,2] - case type - when 'string' - # Unquote - val[1, val.length - 2] - when 'int32' - val.to_i - else - raise "No Ruby type conversion for DBus type '#{type}'" - end - end - if ret.size == 0 - return nil - elsif ret.size == 1 - return ret.first - else - return ret - end -end |