lib/atp/flow.rb in atp-1.1.0 vs lib/atp/flow.rb in atp-1.1.1

- old
+ new

@@ -2,12 +2,10 @@ # Implements the main user API for building and interacting # with an abstract test program class Flow attr_reader :program, :name - attr_accessor :source_file, :source_line_number, :description - CONDITION_KEYS = { if_enabled: :if_enabled, if_enable: :if_enabled, enabled: :if_enabled, enable_flag: :if_enabled, @@ -62,14 +60,18 @@ CONDITION_NODE_TYPES = CONDITION_KEYS.values.uniq def initialize(program, name = nil, options = {}) name, options = nil, name if name.is_a?(Hash) - extract_meta!(options) + @source_file = [] + @source_line_number = [] + @description = [] @program = program @name = name - @pipeline = [n1(:flow, n1(:name, name))] + extract_meta!(options) do + @pipeline = [n1(:flow, n1(:name, name))] + end end # @api private def marshal_dump [@name, @program, Processors::Marshal.new.process(raw)] @@ -211,18 +213,19 @@ # flow.group "RAM Tests" do # flow.test ... # flow.test ... # end def group(name, options = {}) - extract_meta!(options) - apply_conditions(options) do - children = [n1(:name, name)] - children << id(options[:id]) if options[:id] - children << on_fail(options[:on_fail]) if options[:on_fail] - children << on_pass(options[:on_pass]) if options[:on_pass] - g = n(:group, children) - append_to(g) { yield } + extract_meta!(options) do + apply_conditions(options) do + children = [n1(:name, name)] + children << id(options[:id]) if options[:id] + children << on_fail(options[:on_fail]) if options[:on_fail] + children << on_pass(options[:on_pass]) if options[:on_pass] + g = n(:group, children) + append_to(g) { yield } + end end end # Add a test line to the flow # @@ -232,134 +235,135 @@ # @option options [String] :description A description of what the test does, usually formatted in markdown # @option options [Hash] :on_fail What action to take if the test fails, e.g. assign a bin # @option options [Hash] :on_pass What action to take if the test passes # @option options [Hash] :conditions What conditions must be met to execute the test def test(instance, options = {}) - extract_meta!(options) - apply_conditions(options) do - # Allows any continue, bin, or soft bin argument passed in at the options top-level to be assumed - # to be the action to take if the test fails - if b = options.delete(:bin) - options[:on_fail] ||= {} - options[:on_fail][:bin] = b - end - if b = options.delete(:bin_description) - options[:on_fail] ||= {} - options[:on_fail][:bin_description] = b - end - if b = options.delete(:softbin) || b = options.delete(:sbin) || b = options.delete(:soft_bin) - options[:on_fail] ||= {} - options[:on_fail][:softbin] = b - end - if b = options.delete(:softbin_description) || options.delete(:sbin_description) || options.delete(:soft_bin_description) - options[:on_fail] ||= {} - options[:on_fail][:softbin_description] = b - end - if options.delete(:continue) - options[:on_fail] ||= {} - options[:on_fail][:continue] = true - end - if options.key?(:delayed) - options[:on_fail] ||= {} - options[:on_fail][:delayed] = options.delete(:delayed) - end - if f = options.delete(:flag_pass) - options[:on_pass] ||= {} - options[:on_pass][:set_flag] = f - end - if f = options.delete(:flag_fail) - options[:on_fail] ||= {} - options[:on_fail][:set_flag] = f - end + extract_meta!(options) do + apply_conditions(options) do + # Allows any continue, bin, or soft bin argument passed in at the options top-level to be assumed + # to be the action to take if the test fails + if b = options.delete(:bin) + options[:on_fail] ||= {} + options[:on_fail][:bin] = b + end + if b = options.delete(:bin_description) + options[:on_fail] ||= {} + options[:on_fail][:bin_description] = b + end + if b = options.delete(:softbin) || b = options.delete(:sbin) || b = options.delete(:soft_bin) + options[:on_fail] ||= {} + options[:on_fail][:softbin] = b + end + if b = options.delete(:softbin_description) || options.delete(:sbin_description) || options.delete(:soft_bin_description) + options[:on_fail] ||= {} + options[:on_fail][:softbin_description] = b + end + if options.delete(:continue) + options[:on_fail] ||= {} + options[:on_fail][:continue] = true + end + if options.key?(:delayed) + options[:on_fail] ||= {} + options[:on_fail][:delayed] = options.delete(:delayed) + end + if f = options.delete(:flag_pass) + options[:on_pass] ||= {} + options[:on_pass][:set_flag] = f + end + if f = options.delete(:flag_fail) + options[:on_fail] ||= {} + options[:on_fail][:set_flag] = f + end - children = [n1(:object, instance)] + children = [n1(:object, instance)] - name = (options[:name] || options[:tname] || options[:test_name]) - unless name - [:name, :tname, :test_name].each do |m| - name ||= instance.respond_to?(m) ? instance.send(m) : nil + name = (options[:name] || options[:tname] || options[:test_name]) + unless name + [:name, :tname, :test_name].each do |m| + name ||= instance.respond_to?(m) ? instance.send(m) : nil + end end - end - children << n1(:name, name) if name + children << n1(:name, name) if name - num = (options[:number] || options[:num] || options[:tnum] || options[:test_number]) - unless num - [:number, :num, :tnum, :test_number].each do |m| - num ||= instance.respond_to?(m) ? instance.send(m) : nil + num = (options[:number] || options[:num] || options[:tnum] || options[:test_number]) + unless num + [:number, :num, :tnum, :test_number].each do |m| + num ||= instance.respond_to?(m) ? instance.send(m) : nil + end end - end - children << number(num) if num + children << number(num) if num - children << id(options[:id]) if options[:id] + children << id(options[:id]) if options[:id] - if levels = options[:level] || options[:levels] - levels = [levels] unless levels.is_a?(Array) - levels.each do |l| - children << level(l[:name], l[:value], l[:unit] || l[:units]) + if levels = options[:level] || options[:levels] + levels = [levels] unless levels.is_a?(Array) + levels.each do |l| + children << level(l[:name], l[:value], l[:unit] || l[:units]) + end end - end - lims = options[:limit] || options[:limits] - if lims || options[:lo] || options[:low] || options[:hi] || options[:high] - if lims == :none || lims == 'none' - children << n0(:nolimits) - else - lims = Array(lims) unless lims.is_a?(Array) - if lo = options[:lo] || options[:low] - lims << { value: lo, rule: :gte } - end - if hi = options[:hi] || options[:high] - lims << { value: hi, rule: :lte } - end - lims.each do |l| - if l.is_a?(Hash) - children << n(:limit, [l[:value], l[:rule], l[:unit] || l[:units], l[:selector]]) + lims = options[:limit] || options[:limits] + if lims || options[:lo] || options[:low] || options[:hi] || options[:high] + if lims == :none || lims == 'none' + children << n0(:nolimits) + else + lims = Array(lims) unless lims.is_a?(Array) + if lo = options[:lo] || options[:low] + lims << { value: lo, rule: :gte } end + if hi = options[:hi] || options[:high] + lims << { value: hi, rule: :lte } + end + lims.each do |l| + if l.is_a?(Hash) + children << n(:limit, [l[:value], l[:rule], l[:unit] || l[:units], l[:selector]]) + end + end end end - end - if pins = options[:pin] || options[:pins] - pins = [pins] unless pins.is_a?(Array) - pins.each do |p| - if p.is_a?(Hash) - children << pin(p[:name]) - else - children << pin(p) + if pins = options[:pin] || options[:pins] + pins = [pins] unless pins.is_a?(Array) + pins.each do |p| + if p.is_a?(Hash) + children << pin(p[:name]) + else + children << pin(p) + end end end - end - if pats = options[:pattern] || options[:patterns] - pats = [pats] unless pats.is_a?(Array) - pats.each do |p| - if p.is_a?(Hash) - children << pattern(p[:name], p[:path]) - else - children << pattern(p) + if pats = options[:pattern] || options[:patterns] + pats = [pats] unless pats.is_a?(Array) + pats.each do |p| + if p.is_a?(Hash) + children << pattern(p[:name], p[:path]) + else + children << pattern(p) + end end end - end - if options[:meta] - attrs = [] - options[:meta].each { |k, v| attrs << attribute(k, v) } - children << n(:meta, attrs) - end + if options[:meta] + attrs = [] + options[:meta].each { |k, v| attrs << attribute(k, v) } + children << n(:meta, attrs) + end - if subs = options[:sub_test] || options[:sub_tests] - subs = [subs] unless subs.is_a?(Array) - subs.each do |s| - children << s.updated(:sub_test, nil) + if subs = options[:sub_test] || options[:sub_tests] + subs = [subs] unless subs.is_a?(Array) + subs.each do |s| + children << s.updated(:sub_test, nil) + end end - end - children << on_fail(options[:on_fail]) if options[:on_fail] - children << on_pass(options[:on_pass]) if options[:on_pass] + children << on_fail(options[:on_fail]) if options[:on_fail] + children << on_pass(options[:on_pass]) if options[:on_pass] - save_conditions - n(:test, children) + save_conditions + n(:test, children) + end end end # Equivalent to calling test, but returns a sub_test node instead of adding it to the flow. # @@ -372,16 +376,17 @@ def bin(number, options = {}) if number.is_a?(Hash) fail 'The bin number must be passed as the first argument' end options[:bin_description] ||= options.delete(:description) - extract_meta!(options) - apply_conditions(options) do - options[:type] ||= :fail - options[:bin] = number - options[:softbin] ||= options[:soft_bin] || options[:sbin] - set_result(options[:type], options) + extract_meta!(options) do + apply_conditions(options) do + options[:type] ||= :fail + options[:bin] = number + options[:softbin] ||= options[:soft_bin] || options[:sbin] + set_result(options[:type], options) + end end end def pass(number, options = {}) if number.is_a?(Hash) @@ -390,61 +395,68 @@ options[:type] = :pass bin(number, options) end def cz(instance, cz_setup, options = {}) - extract_meta!(options) - apply_conditions(options) do - node = n1(:cz, cz_setup) - append_to(node) { test(instance, options) } + extract_meta!(options) do + apply_conditions(options) do + node = n1(:cz, cz_setup) + append_to(node) { test(instance, options) } + end end end alias_method :characterize, :cz # Append a log message line to the flow def log(message, options = {}) - extract_meta!(options) - apply_conditions(options) do - n1(:log, message.to_s) + extract_meta!(options) do + apply_conditions(options) do + n1(:log, message.to_s) + end end end # Enable a flow control variable def enable(var, options = {}) - extract_meta!(options) - apply_conditions(options) do - n1(:enable, var) + extract_meta!(options) do + apply_conditions(options) do + n1(:enable, var) + end end end # Disable a flow control variable def disable(var, options = {}) - extract_meta!(options) - apply_conditions(options) do - n1(:disable, var) + extract_meta!(options) do + apply_conditions(options) do + n1(:disable, var) + end end end def set_flag(flag, options = {}) - extract_meta!(options) - apply_conditions(options) do - set_flag_node(flag) + extract_meta!(options) do + apply_conditions(options) do + set_flag_node(flag) + end end end # Insert explicitly rendered content in to the flow def render(str, options = {}) - extract_meta!(options) - apply_conditions(options) do - n1(:render, str) + extract_meta!(options) do + apply_conditions(options) do + n1(:render, str) + end end end def continue(options = {}) - extract_meta!(options) - apply_conditions(options) do - n0(:continue) + extract_meta!(options) do + apply_conditions(options) do + n0(:continue) + end end end # Execute the given flow in the console def run(options = {}) @@ -487,41 +499,54 @@ ATP::AST::Extractor.new.process(raw, [:id]).map { |node| node.to_a[0] } end private + def description + @description.last + end + + def source_file + @source_file.last + end + + def source_line_number + @source_line_number.last + end + def flow_control_method(name, flag, options = {}, &block) - extract_meta!(options) - if flag.is_a?(Array) - if name == :if_passed - fail 'if_passed only accepts one ID, use if_any_passed or if_all_passed for multiple IDs' - end - if name == :if_failed - fail 'if_failed only accepts one ID, use if_any_failed or if_all_failed for multiple IDs' - end - end - apply_conditions(options) do - if block - node = n1(name, flag) - open_conditions << [name, flag] - node = append_to(node) { block.call } - open_conditions.pop - else - unless options[:then] || options[:else] - fail "You must supply a :then or :else option when calling #{name} like this!" + extract_meta!(options) do + if flag.is_a?(Array) + if name == :if_passed + fail 'if_passed only accepts one ID, use if_any_passed or if_all_passed for multiple IDs' end - node = n1(name, flag) - if options[:then] - node = append_to(node) { options[:then].call } + if name == :if_failed + fail 'if_failed only accepts one ID, use if_any_failed or if_all_failed for multiple IDs' end - if options[:else] - e = n0(:else) - e = append_to(e) { options[:else].call } - node = node.updated(nil, node.children + [e]) + end + apply_conditions(options) do + if block + node = n1(name, flag) + open_conditions << [name, flag] + node = append_to(node) { block.call } + open_conditions.pop + else + unless options[:then] || options[:else] + fail "You must supply a :then or :else option when calling #{name} like this!" + end + node = n1(name, flag) + if options[:then] + node = append_to(node) { options[:then].call } + end + if options[:else] + e = n0(:else) + e = append_to(e) { options[:else].call } + node = node.updated(nil, node.children + [e]) + end end + node end - node end end def apply_conditions(options, node = nil) # Applying the current context, means to append to the same node as the last time, this @@ -627,12 +652,16 @@ def condition_node?(node) !!CONDITION_KEYS[node.type] end def extract_meta!(options) - self.source_file = options.delete(:source_file) - self.source_line_number = options.delete(:source_line_number) - self.description = options.delete(:description) + @source_file << options.delete(:source_file) + @source_line_number << options.delete(:source_line_number) + @description << options.delete(:description) + yield + @source_file.pop + @source_line_number.pop + @description.pop end def id(name) n1(:id, name) end