diff --git a/bin/testml-cli.bash b/bin/testml-cli.bash index e9f5d9b2..e2a1baa2 100644 --- a/bin/testml-cli.bash +++ b/bin/testml-cli.bash @@ -137,8 +137,9 @@ TestML supports the following runners: node-tap NodeJS w/ TAP perl-tap Perl w/ TAP python-tap Python (2 or 3) w/ TAP - python-tap Python (2 or 3) w/ unittest + python-unit Python (2 or 3) w/ unittest raku-tap Raku w/ TAP + ruby-tap Ruby w/ TAP Aliases: coffee Alias for coffee-mocha diff --git a/src/ruby/bin/testml-ruby-tap b/src/ruby/bin/testml-ruby-tap index 87e6b889..38c669ea 100755 --- a/src/ruby/bin/testml-ruby-tap +++ b/src/ruby/bin/testml-ruby-tap @@ -7,7 +7,7 @@ set -e -u -o pipefail testml-run-file() { # set -x RUBYLIB=$TESTML_ROOT_LIB:$TESTML_LIB${RUBYLIB:+:$RUBYLIB} \ - ${TESTML_LANG} -e "require 'testml/run/tap'; $TESTML_MODULE.run('$1')" + ${TESTML_LANG} -rtestml -e "$TESTML_MODULE.run('$1')" } [[ ${TESTML_SOURCED-} ]] || @@ -17,7 +17,7 @@ source-testml-config : "${TESTML_BIN:=testml-ruby-tap}" : "${TESTML_LANG:=ruby}" -: "${TESTML_MODULE:=TestML::Run::TAP}" -: "${TESTML_BRIDGE:=testml-bridge}" +: "${TESTML_MODULE:=TestML}" +: "${TESTML_BRIDGE:=testml/bridge}" [[ $0 != "${BASH_SOURCE[0]}" ]] || testml-run "$@" diff --git a/src/ruby/lib/testml.rb b/src/ruby/lib/testml.rb new file mode 100644 index 00000000..7f75762d --- /dev/null +++ b/src/ruby/lib/testml.rb @@ -0,0 +1,291 @@ +# frozen_string_literal: true + +require "json" + +module TestML + def self.run(filepath) + json = JSON.parse(File.read(filepath)) + raise "Unsupported TestML version: #{json["testml"]}" if json["testml"] != "0.3.0" + + index = Index.new + runner = Runner.new(index) + + runner.run(json["code"], json["data"]) + puts "1..#{index}" + end + + class Index + def initialize = @value = 0 + def succ! = @value = @value.succ + def to_s = "#{@value}" + end + + Mine = Class.new + NULL = Object.new + NONE = Object.new + FALSY = [nil, false, NONE, NULL].freeze + + class Bridge + def add(left, right) + left + right + end + + def cat(*values) + values.join + end + + def get_env(key) + ENV.fetch(key) { NONE } + end + + def mine + Mine.new + end + + def sub(left, right) + left - right + end + end + + class Runner + def initialize(index) + @index = index + @vars = {} + @bridge = nil + @label = nil + @diff = false + @plan = false + end + + def run(exprs, env) + exprs.each { |expr| compile(expr, env) } + end + + private + + def bridge + @bridge ||= + begin + require ENV.fetch("TESTML_BRIDGE") + TestML::Bridge.new + end + end + + def join_label(parent, child) + child.gsub(/(\A\+|\+\z|\{\+\})/, parent) + end + + def assert(positive, got, operator, want, label, env) + if env.is_a?(Hash) + label ||= env["Label"] + label = join_label(label, @label) if @label + end + + label ||= "" + label = label.gsub(/\{(\*?.+?)\}/) do + substitution = + case (value = $1) + when "Got" then got + when "Want" then want + when /^\*(.+)$/ then env.fetch($1) + else @vars.fetch(value) + end + + case substitution + when String + substitution.gsub("\n", "␤") + when Array + "[#{substitution.map(&:inspect).join(",")}]" + else + substitution.inspect + end + end + + @index.succ! + + result = got.public_send(operator, want) + result = !result unless positive + + if result + puts "ok #{@index} - #{label}" + else + puts "not ok #{@index} - #{label}" + + if @diff + puts " got: '#{got.inspect}'" + puts " expected: '#{want.inspect}'" + end + end + end + + def filters?(filters, env) + filters.all? do |filter| + case filter + in /^\*(.+)$/ then env.key?($1) + in /^\!\*(.+)$/ then !env.key?($1) + end + end + end + + def compile(expr, env) + case expr + + # functions + in ["ArgV"] then ARGV + in ["Bool", value] then !FALSY.include?(compile(value, env)) + in ["Block"] then env + in ["Block", label] then env.find { |env| env["Label"] == label } + in ["Blocks"] then env + in ["Cat", *values] then values.map { |value| compile(value, env) }.join + in ["Env"] then ENV + in ["Error"] then StandardError.new + in ["Error", message] then StandardError.new(message) + in ["False"] then false + in ["None"] then NONE + in ["Null"] then NULL + in ["Sum", *values] then values.sum { |value| compile(value, env) } + in ["Throw", message] then raise StandardError, message + in ["True"] then true + + # assignments + in ["=", "Label", value] + @label = value + in ["=", "Diff", value] + @diff = compile(value, env) + in ["=", "Plan", value] + @plan = compile(value, env) + in ["=", name, value] + @vars[name] = compile(value, env) + in ["||=", name, value] + @vars[name] = compile(value, env) if FALSY.include?(@vars[name]) + + # statements + in ["*", name] + compile(env.fetch(name), env) + in ["%<>", filters, ["=>", *] => function] + env.each do |child_env| + compile(function, child_env).call([], child_env) if filters?(filters, child_env) + end + in ["%<>", filters, expr] + env.each do |child_env| + if expr.length == 4 + child_env = { **child_env } + child_env["Label"] = join_label(child_env["Label"], expr[3]) + end + + compile(expr, child_env) if filters?(filters, child_env) + end + in ["<>", filters, ["=>", *] => function] + compile(function, env).call([], env) if filters?(filters, env) + in ["<>", filters, expr] + compile(expr, env) if filters?(filters, env) + in ["<>", filters, expr, label] + compile(expr, env.merge("Label" => join_label(env["Label"], label))) if filters?(filters, env) + in [".", receiver, *calls] + compile_calls(receiver, calls, env) + in ["%", inputs, function] + callable = compile(function, env) + compile(inputs, env).each { |input| callable.call([[input]], {}) } + in ["&", expr] + compile(expr, env).call([], env) + in ["[]", receiver, index] + compile(receiver, env).fetch(compile(index, env)) { NONE } + in [":" | /\Ahash-lookup\z/i, receiver, key] + compile(receiver, env).fetch(compile(key, env)) { NONE } + + # assertions + in [("==" | "!==") => operator, left, right, *labels] + assert(operator == "==", compile(left, env), :==, compile(right, env), labels[0], env) + in [("=~" | "!=~") => operator, left, right, *labels] + left = compile(left, env) + right = compile(right, env) + + Array(right).each do |right| + Array(left).each do |left| + assert(operator == "=~", left, :match?, right, labels[0], env) + end + end + in [("~~" | "!~~") => operator, left, right, *labels] + left = compile(left, env) + right = compile(right, env) + + Array(right).each do |substring| + assert(operator == "~~", left, :include?, substring, labels[0], env) + end + + # types + in [Array => values] then values.map { |value| compile(value, env) } + in [Hash => values] then values.transform_values { |value| compile(value, env) } + in String then expr + in Integer then expr + in ["/", pattern] then Regexp.new(pattern) + in ["\"", value] then value.gsub(/\{(.+?)\}/) { @vars.fetch($1) } + in ["_"] then env["inputs"][0][0] + in ["=>", params, exprs] + runner = Runner.new(@index) + exprs = compile_params(params).concat(exprs) + ->(inputs, env) { runner.run(exprs, env.merge("inputs" => inputs)) } + in [String => name] if @vars.key?(name) + @vars.fetch(name) + in [String => name, *arguments] if @vars.key?(name) + @vars.fetch(name).call([arguments.map { |argument| compile(argument, env) }], {}) + in [/\A[a-z]/ => name, *arguments] + bridge.public_send(name.tr("-", "_"), *arguments.map { |argument| compile(argument, env) }) + end + end + + def compile_params(params) + params.map.with_index { |param, index| ["=", param, ["[]", ["*", "inputs"], index]] } + end + + def compile_calls(callee, calls, env) + calls.inject(-> { compile(callee, env) }) do |callee, call| + -> { + case call + in ["Catch"] + begin callee.call; rescue => error then error end + in ["Count"] + callee.call.count + in ["Join", delimiter] + callee.call.join(delimiter) + in ["Lines"] + callee.call.split("\n") + in ["Msg"] + callee.call.message + in ["Split", delimiter] + callee.call.split(delimiter) + in ["Text"] + callee.call.map { |line| "#{line}\n" }.join + in ["Type"] + compile_type(callee.call) + in ["=>", params, exprs] + Runner.new(@index).run(compile_params(params).concat(exprs), { "inputs" => [callee.call] }) + in [String => name, *arguments] if @vars.key?(name) + @vars.fetch(name).call([[callee.call, *arguments]], {}) + in [/\A[a-z]/ => name, *arguments] + bridge.public_send(name.tr("-", "_"), callee.call, *arguments.map { |argument| compile(argument, env) }) + else + begin callee.call; rescue => error then raise end + raise StandardError, "Unknown call: #{call.inspect}" + end + } + end.call + end + + def compile_type(object) + case object + in Array then "list" + in Hash then "hash" + in Integer then "num" + in Mine then "native" + in NONE then "none" + in NULL then "null" + in Proc then "func" + in Regexp then "regex" + in StandardError then "error" + in String then "str" + in TrueClass | FalseClass then "bool" + end + end + end +end diff --git a/src/ruby/lib/testml/bridge.rb b/src/ruby/lib/testml/bridge.rb index 64860bd3..e69de29b 100644 --- a/src/ruby/lib/testml/bridge.rb +++ b/src/ruby/lib/testml/bridge.rb @@ -1,10 +0,0 @@ -use strict; use warnings; -package TestML::Bridge; - -sub new { - my $class = shift; - - bless {@_}, $class; -} - -1; diff --git a/src/ruby/lib/testml/run.rb b/src/ruby/lib/testml/run.rb deleted file mode 100644 index 7bc02302..00000000 --- a/src/ruby/lib/testml/run.rb +++ /dev/null @@ -1,673 +0,0 @@ -require 'json' - -class TestML -end -class TestML::Null -end - -class TestML::Run - -# use strict; use warnings; -# package TestMLFunction; - -# sub new { -# my ($class, $func) = @_; -# return bless {func => $func}, $class; -# } - -# package TestML::Run; - -# use JSON::PP; - -# use utf8; -# use TestML::Boolean; -# use Scalar::Util; - -# # use XXX; - - @@Null = TestML::Null.new - - @@vtable = { - '==' => 'assert_eq', - '~~' => 'assert_has', - '=~' => 'assert_like', - '!==' => 'assert_not_eq', - '!~~' => 'assert_not_has', - '!=~' => 'assert_not_like', - - '.' => 'exec_dot', - '%' => 'each_exec', - '%<>' => 'each_pick', - '<>' => 'pick_exec', - '&' => 'call_func', - - '"' => 'get_str', - ':' => 'get_hash', - '[]' => 'get_list', - '*' => 'get_point', - - '=' => 'set_var', - '||=' => 'or_set_var', - } - - @@types = { - '=>' => 'func', - '/' => 'regex', - '!' => 'error', - '?' => 'native', - } - -# #------------------------------------------------------------------------------ - def initialize(params) - @vars = {} - -# my $testml = $params{testml}; - -# return bless { -# file => $params{file}, -# ast => $params{testml}, - -# bridge => $params{bridge}, -# stdlib => $params{stdlib}, - -# vars => {}, -# block => undef, -# warned_only => false, -# error => undef, -# thrown => undef, -# }, $class; - end - - def from_file(file) - - @file = file - @ast = JSON.load File.read file - return self - - end - - def test - self.testml_begin - - - @ast['code'].each do |statement| - #require 'pry'; binding.pry - self.exec_expr(statement) - end - - self.testml_end - - return - end - -# #------------------------------------------------------------------------------ - def exec(expr) - - return self.exec_expr(expr)[0] - end - - def exec_expr(expr, context=[]) - - return [expr] unless self.type(expr) == 'expr' - - args = expr.clone - name = args.shift - opcode = name - if call = @@vtable[ opcode ] - call = call[0] if call.class == Array - ret = self.public_send(call, *args) - else - args.unshift *context - - if (@vars.key? name) - value = @vars[name] - - if args.length > 0 - throw "Variable '#{name}' has args but is not a function" \ - unless self.type(value) == 'func' - ret = self.exec_func value, args - else - ret = [value] - end - throw ret - - elsif name.match /^[a-z]/ - ret = self.call_bridge name, *args -# } -# elsif ($name =~ /^[A-Z]/) { -# @ret = $self->call_stdlib($name, @args); -# } -# else { -# die "Can't resolve TestML function '$name'"; -# } - end - end - - return [*ret] - end - -# sub exec_func { -# my ($self, $function, $args) = @_; -# $args = [] unless defined $args; - -# my ($op, $signature, $statements) = @$function; - -# if (@$signature > 1 and @$args == 1 and $self->type($args) eq 'list') { -# $args = $args->[0]; -# } - -# die "TestML function expected '${\scalar @$signature}' arguments, but was called with '${\scalar @$args}' arguments" -# if @$signature != @$args; - -# my $i = 0; -# for my $v (@$signature) { -# $self->{vars}{$v} = $self->exec($args->[$i++]); -# } - -# for my $statement (@$statements) { -# $self->exec_expr($statement); -# } - -# return; -# } - -# #------------------------------------------------------------------------------ - def call_bridge(name, *args) - - if not @bridge - bridge_module = ENV["TESTML_BRIDGE"] - bridge_module = 'testml-bridge' if bridge_module == nil - - if @ast['bridge'] and @ast['bridge'].key? 'ruby' - code = @ast['bridge']['ruby'] -# eval <<"..." or die $@; -# use strict; use warnings; -# package TestMLBridge; -# use base 'TestML::Bridge'; -# $code; -# 1; -# ... -# } - else - require bridge_module - end - - @bridge = TestMLBridge.new - end - - call = name.gsub /-/, '_' - - throw "Can't find bridge function: '#{name}'" \ - unless @bridge and @bridge.respond_to? call - - - args = args.map { |arg| self.uncook self.exec arg } - - ret = @bridge.public_send call, *args - - return unless ret != nil - - self.cook(ret) - end - -# sub call_stdlib { -# my ($self, $name, @args) = @_; - -# if (not $self->{stdlib}) { -# require TestML::StdLib; -# $self->{stdlib} = TestML::StdLib->new($self); -# } - -# my $call = lc $name; -# die "Unknown TestML Standard Library function: '$name'" -# unless $self->{stdlib}->can($call); - -# @args = map {$self->uncook($self->exec($_))} @args; - -# $self->cook($self->{stdlib}->$call(@args)); -# } - -# #------------------------------------------------------------------------------ - def assert_eq(left, right, label=nil, not_=false) - got = @vars["Got"] = self.exec(left) - want = @vars["Want"] = self.exec(right) - - method = self.get_method('assert_%s_eq_%s', got, want) - self.public_send method, got, want, label, not_ - - return - end - - def assert_str_eq_str(got, want, label, not_=false) - self.testml_eq got, want, self.get_label(label), not_ - end - - def assert_num_eq_num(got, want, label, not_) - self.testml_eq got, want, self.get_label(label), not_ - end - -# sub assert_bool_eq_bool { -# my ($self, $got, $want, $label, $not) = @_; -# $self->testml_eq($got, $want, $self->get_label($label), $not); -# } - - -# sub assert_has { -# my ($self, $left, $right, $label, $not) = @_; -# my $got = $self->exec($left); -# my $want = $self->exec($right); -# my $method = $self->get_method('assert_%s_has_%s', $got, $want); -# $self->$method($got, $want, $label, $not); -# return; -# } - -# sub assert_str_has_str { -# my ($self, $got, $want, $label, $not) = @_; -# $self->{vars}{Got} = $got; -# $self->{vars}{Want} = $want; -# $self->testml_has($got, $want, $self->get_label($label), $not); -# } - -# sub assert_str_has_list { -# my ($self, $got, $want, $label, $not) = @_; -# for my $str (@{$want->[0]}) { -# $self->assert_str_has_str($got, $str, $label, $not); -# } -# } - -# sub assert_list_has_str { -# my ($self, $got, $want, $label, $not) = @_; -# $self->{vars}{Got} = $got; -# $self->{vars}{Want} = $want; -# $self->testml_list_has($got->[0], $want, $self->get_label($label), $not); -# } - -# sub assert_list_has_list { -# my ($self, $got, $want, $label, $not) = @_; -# for my $str (@{$want->[0]}) { -# $self->assert_list_has_str($got, $str, $label, $not); -# } -# } - - -# sub assert_like { -# my ($self, $left, $right, $label, $not) = @_; -# my $got = $self->exec($left); -# my $want = $self->exec($right); -# my $method = $self->get_method('assert_%s_like_%s', $got, $want); -# $self->$method($got, $want, $label, $not); -# return; -# } - -# sub assert_str_like_regex { -# my ($self, $got, $want, $label, $not) = @_; -# $self->{vars}{Got} = $got; -# $self->{vars}{Want} = "/${\ $want->[1]}/"; -# $want = $self->uncook($want); -# $self->testml_like($got, $want, $self->get_label($label), $not); -# } - -# sub assert_str_like_list { -# my ($self, $got, $want, $label, $not) = @_; -# for my $regex (@{$want->[0]}) { -# $self->assert_str_like_regex($got, $regex, $label, $not); -# } -# } - -# sub assert_list_like_regex { -# my ($self, $got, $want, $label, $not) = @_; -# for my $str (@{$got->[0]}) { -# $self->assert_str_like_regex($str, $want, $label, $not); -# } -# } - -# sub assert_list_like_list { -# my ($self, $got, $want, $label, $not) = @_; -# for my $str (@{$got->[0]}) { -# for my $regex (@{$want->[0]}) { -# $self->assert_str_like_regex($str, $regex, $label, $not); -# } -# } -# } - -# sub assert_not_eq { -# my ($self, $got, $want, $label) = @_; -# $self->assert_eq($got, $want, $label, true); -# } - -# sub assert_not_has { -# my ($self, $got, $want, $label) = @_; -# $self->assert_has($got, $want, $label, true); -# } - -# sub assert_not_like { -# my ($self, $got, $want, $label) = @_; -# $self->assert_like($got, $want, $label, true); -# } - -# #------------------------------------------------------------------------------ - def exec_dot(*args) - - context = [] - - @error = nil - - args.each do |call| -# if (not $self->{error}) { -# eval { - if self.type(call) == 'func' - throw 'todo' -# $self->exec_func($call, $context->[0]); -# $context = []; - else - context = self.exec_expr call, context - end -# }; -# if ($@) { -# if ($ENV{TESTML_DEVEL}) { -# require Carp; -# Carp::cluck($@); -# } -# $self->{error} = $self->call_stdlib('Error', "$@"); -# } -# elsif ($self->{thrown}) { -# $self->{error} = $self->cook(delete $self->{thrown}); -# } -# } -# else { -# if ($call->[0] eq 'Catch') { -# $context = [delete $self->{error}]; -# } -# } - end - -# die "Uncaught Error: ${\ $self->{error}[1]{msg}}" -# if $self->{error}; - - return context[0] - end - -# sub each_exec { -# my ($self, $list, $expr) = @_; -# $list = $self->exec($list); -# $expr = $self->exec($expr); - -# for my $item (@{$list->[0]}) { -# $self->{vars}{_} = [$item]; -# if ($self->type($expr) eq 'func') { -# if (@{$expr->[1]} == 0) { -# $self->exec_func($expr); -# } -# else { -# $self->exec_func($expr, [$item]); -# } -# } -# else { -# $self->exec_expr($expr); -# } -# } -# } - - def each_pick(list, expr) - - @ast["data"].each do |block| - @block = block - - if @block["ONLY"] and not @warned_only - self.err("Warning: TestML 'ONLY' in use") - @warned_only = true - end - - self.exec_expr ['<>', list, expr] - end - - @block = nil - - return - end - - def pick_exec(list, expr) - pick = true - - list.each do |point| - if point.match /^\*/ and not @block.key? point[1..-1] or - point.match /^!*/ and @block.key? point[2..-1] - throw point - pick = false - break - end - end - - if pick - if self.type(expr) == 'func' - self.exec_func expr - else - self.exec_expr expr - end - end - - return - end - -# sub call_func { -# my ($self, $func) = @_; -# my $name = $func->[0]; -# $func = $self->exec($func); -# die "Tried to call '$name' but is not a function" -# unless defined $func and $self->type($func) eq 'func'; -# $self->exec_func($func); -# } - -# sub get_str { -# my ($self, $string) = @_; -# $self->interpolate($string); -# } - -# sub get_hash { -# my ($self, $hash, $key) = @_; -# $hash = $self->exec($hash); -# $key = $self->exec($key); -# $self->cook($hash->[0]{$key}); -# } - -# sub get_list { -# my ($self, $list, $index) = @_; -# $list = $self->exec($list); -# return [] if not @{$list->[0]}; -# $self->cook($list->[0][$index]); -# } - - def get_point(name) - return self.getp name - end - -# sub set_var { -# my ($self, $name, $expr) = @_; - -# $self->setv($name, $self->exec($expr)); - -# return; -# } - -# sub or_set_var { -# my ($self, $name, $expr) = @_; -# return if defined $self->{vars}{$name}; - -# if ($self->type($expr) eq 'func') { -# $self->setv($name, $expr); -# } -# else { -# $self->setv($name, $self->exec($expr)); -# } -# return; -# } - -# #------------------------------------------------------------------------------ - def getp(name) - return unless @block - value = @block[name] - value = self.exec value if defined? value - return value - end - -# sub getv { -# my ($self, $name) = @_; -# $self->{vars}{$name}; -# } - -# sub setv { -# my ($self, $name, $value) = @_; -# $self->{vars}{$name} = $value; -# return; -# } - -# #------------------------------------------------------------------------------ - def type(value) - - return 'null' if value == nil - - if value.class == String - return 'str' - elsif value.kind_of? Integer - return 'num' - elsif value.class == Float - return 'num' - elsif value.class == TrueClass - return 'bool' - elsif value.class == FalseClass - return 'bool' - end - - if value.class == Array - return 'none' if value.length == 0 - return @@types[ value[0] ] if @@types.key? value[0] - return 'list' if value[0].class == Array - return 'hash' if value[0].class == Hash - return 'expr' - end - - throw "Can't determine type of this value: '#{value}'" - end - - def cook(value) - - return value if value.kind_of? Integer - throw "not implemented yet" -# return [] if not @value; -# my $value = $value[0]; -# return undef if not defined $value; - -# return $value if not ref $value; -# return [$value] if ref($value) =~ /^(?:HASH|ARRAY)$/; -# return $value if isBoolean($value); -# return ['/', $value] if ref($value) eq 'Regexp'; -# return ['!', $value] if ref($value) eq 'TestMLError'; -# return $value->{func} if ref($value) eq 'TestMLFunction'; -# return ['?', $value]; - end - - def uncook(value) - - type = self.type value - - return value if type.match /^(?:str|num|bool|null)$/; - throw "hahaha" -# return $value->[0] if $type =~ /^(?:list|hash)$/; -# return $value->[1] if $type =~ /^(?:error|native)$/; -# return TestMLFunction->new($value) if $type eq 'func'; -# if ($type eq 'regex') { -# return ref($value->[1]) eq 'Regexp' -# ? $value->[1] -# : qr/${\ $value->[1]}/; -# } -# return () if $type eq 'none'; - -# require XXX; -# XXX::ZZZ("Can't uncook this value of type '$type':", $value); - end - -#------------------------------------------------------------------------------ - def get_method(pattern, *args) - method = sprintf pattern, *(args.map {|a| self.type a}) - - throw "Method '#{method}' does not exist" \ - unless self.respond_to?(method) - - return method - end - - def get_label(label_expr) - label_expr = '' unless defined? label_expr - - label = self.exec label_expr - -# $label ||= $self->getv('Label') || ''; - - block_label = @block ? @block['Label'] : '' - - if label -# $label =~ s/^\+/$block_label/; -# $label =~ s/\+$/$block_label/; -# $label =~ s/\{\+\}/$block_label/; - - else - label = block_label - label = '' unless defined? label - end - - return label -# return $self->interpolate($label, true); - end - -# sub interpolate { -# my ($self, $string, $label) = @_; -# # XXX Hack to see input file in label: -# $self->{vars}{File} = $ENV{TESTML_FILEVAR}; - -# $string =~ s/\{([\-\w]+)\}/$self->transform1($1, $label)/ge; -# $string =~ s/\{\*([\-\w]+)\}/$self->transform2($1, $label)/ge; - -# return $string; -# } - -# sub transform { -# my ($self, $value, $label) = @_; -# my $type = $self->type($value); -# if ($label) { -# if ($type =~ /^(?:list|hash)$/) { -# return encode_json($value->[0]); -# } -# if ($type eq 'regex') { -# return "$value->[1]"; -# } -# $value =~ s/\n/␤/g; -# return "$value"; -# } -# else { -# if ($type =~ /^(?:list|hash)$/) { -# return encode_json($value->[0]); -# } -# else { -# return "$value"; -# } -# } -# } - -# sub transform1 { -# my ($self, $name, $label) = @_; -# my $value = $self->{vars}{$name}; -# return '' unless defined $value; -# $self->transform($value, $label); -# } - -# sub transform2 { -# my ($self, $name, $label) = @_; -# return '' unless $self->{block}; -# my $value = $self->{block}{point}{$name}; -# return '' unless defined $value; -# $self->transform($value, $label); -# } - -end - -# vim: set sw=2 sts=2 et: diff --git a/src/ruby/lib/testml/run/tap.rb b/src/ruby/lib/testml/run/tap.rb deleted file mode 100644 index eb9a3b9f..00000000 --- a/src/ruby/lib/testml/run/tap.rb +++ /dev/null @@ -1,241 +0,0 @@ -require 'testml/run' - -class TestML::Run::TAP < TestML::Run - def self.run(file) - TestML::Run::TAP.new.from_file(file).test; - end - - def initialize(params={}) - @count = 0 - super -# my $self = $class->SUPER::new(@params); -# -# return $self; - return self - end - - def testml_begin - @checked = false - @planned = false - end - - def testml_end - self.tap_done \ - unless @planned - end - - def testml_eq(got, want, label, not_=false) - self.check_plan - - self.tap_is got, want, label - end -# if (not $not and -# $got ne $want and -# $want =~ /\n/ and -# (not defined $self->getv('Diff') or $self->getv('Diff')) and -# not($ENV{TESTML_NO_DIFF}) -# ) { -# require Text::Diff; -# -# $self->tap_ok(0, $label ? ($label) : ()); -# -# my $diff = Text::Diff::diff( -# \$want, -# \$got, -# { -# FILENAME_A => 'want', -# FILENAME_B => 'got', -# } -# ); -# -# $self->tap_diag($diff); -# } -# elsif ($not) { -# $self->tap_isnt($got, $want, $label ? ($label) : ()); -# } -# else { -# $self->tap_is($got, $want, $label ? ($label) : ()); -# } -# } -# -# sub testml_like { -# my ($self, $got, $want, $label, $not) = @_; -# $self->check_plan; -# -# if ($not) { -# $self->tap_unlike($got, $want, $label); -# } -# else { -# $self->tap_like($got, $want, $label); -# } -# } -# -# sub testml_has { -# my ($self, $got, $want, $label, $not) = @_; -# $self->check_plan; -# -# my $index = index($got, $want); -# if ($not ? ($index == -1) : ($index != -1)) { -# $self->tap_ok(1, $label); -# } -# else { -# $self->tap_ok(0, $label); -# my $verb = $not ? ' does' : "doesn't"; -# $self->tap_diag(" this string: '$got'\n $verb contain: '$want'"); -# } -# } -# -# sub testml_list_has { -# my ($self, $got, $want, $label) = @_; -# $self->check_plan; -# -# for my $str (@$got) { -# next if ref $str; -# if ($str eq $want) { -# $self->tap_ok(1, $label); -# return; -# } -# } -# $self->tap_ok(0, $label); -# $self->tap_diag(" this list: @$got\n doesn't contain: $want"); -# } -# - def check_plan - return if @checked - @checked = true - end - -# sub check_plan { -# my ($self) = @_; -# -# return if $self->{checked}; -# $self->{checked} = 1; -# -# if (my $plan = $self->{vars}{Plan}) { -# $self->{planned} = 1; -# $self->tap_plan($plan); -# } -# } -# -# sub tap_plan { -# my ($self, $plan) = @_; -# $self->out("1..$plan"); -# } - - def tap_pass(label='') - label = " - #{label}" if label != ''; - @count += 1 - self.out "ok #{@count}#{label}" - return - end - - def tap_fail(label='') - label = " - #{label}" if label != ''; - @count += 1 - self.out "not ok #{@count}#{label}" - end - - def tap_ok(ok, label) - if ok - self.tap_pass label - else - self.tap_fail label - end - end - - def tap_is(got, want, label) - ok = got == want - if ok - self.tap_pass label - else - self.tap_fail label - end - end -# $self->show_error( -# ' got:', $got, -# ' expected:', $want, -# $label, -# ); -# } -# } -# -# sub tap_isnt { -# my ($self, $got, $want, $label) = @_; -# my $ok = $got ne $want; -# if ($ok) { -# $self->tap_pass($label); -# } -# else { -# $self->tap_fail($label); -# $self->show_error( -# ' got:', $got, -# ' expected:', 'anything else', -# $label, -# ); -# } -# } -# -# sub tap_like { -# my ($self, $got, $want, $label) = @_; -# if ($got =~ $want) { -# $self->tap_pass($label); -# } -# else { -# $self->tap_fail($label); -# } -# } -# -# sub tap_unlike { -# my ($self, $got, $want, $label) = @_; -# if ($got !~ $want) { -# $self->tap_pass($label); -# } -# else { -# $self->tap_fail($label); -# } -# } -# -# sub tap_diag { -# my ($self, $msg) = @_; -# my $str = $msg; -# $str =~ s/^/# /mg; -# $self->err($str); -# } - - def tap_done - self.out "1..#{@count}" - end - -# sub show_error { -# my ($self, $got_prefix, $got, $want_prefix, $want, $label) = @_; -# if ($label) { -# $self->err("# Failed test '$label'"); -# } -# else { -# $self->err("# Failed test"); -# } -# -# if (not ref $got) { -# $got = "'$got'" -# } -# $self->tap_diag("$got_prefix $got"); -# -# if (not ref $want) { -# $want = "'$want'" -# } -# $self->tap_diag("$want_prefix $want"); -# } - - def out(str) - # TODO -# local $| = 1; -# binmode STDOUT, ':utf8'; - puts str - end - - def err(str) - $stderr.puts str - end -end - -# vim: set sw=2 sts=2 et: diff --git a/src/ruby/lib/testml/stdlib.rb b/src/ruby/lib/testml/stdlib.rb deleted file mode 100644 index 5bed4bc6..00000000 --- a/src/ruby/lib/testml/stdlib.rb +++ /dev/null @@ -1,158 +0,0 @@ -use strict; use warnings; -package TestML::StdLib; -use TestML::Boolean(); - -sub new { - my ($class, $run) = @_; - bless {run => $run}, $class; -} - -sub argv { - [@ARGV]; -} - -sub block { - my ($self, $selector) = @_; - return $self->{run}{block} - if not defined $selector; - for my $block (@{$self->{run}{ast}{data}}) { - if ($block->{label} eq $selector) { - return $block; - } - } - return undef; -} - -sub blocks { - my ($self) = @_; - [@{$self->{run}{ast}{data}}]; -} - -sub bool { - my ($self, $value) = @_; - (defined($value) and not TestML::Boolean::isFalse($value)) - ? TestML::Boolean::true - : TestML::Boolean::false; -} - -sub cat { - my ($self, @strings) = @_; - my $strings = ref($strings[0]) eq 'ARRAY' - ? $strings[0] - : [@strings]; - CORE::join '', @$strings; -} - -sub count { - my ($self, $list) = @_; - scalar @$list; -} - -sub env { - \%ENV; -} - -sub error { - my ($self, $msg) = (@_, ''); - TestMLError->new($msg); -} - -sub false { - TestML::Boolean::false(); -} - -sub fromjson { - my ($self, $value) = @_; - $self->_json->decode($value); -} - -sub join { - my ($self, $list, $separator) = @_; - $separator = ' ' unless defined $separator; - CORE::join $separator, @$list; -} - -sub lines { - my ($self, $text) = @_; - chomp $text; - [split "\n", $text]; -} - -sub msg { - my ($self, $error) = (@_); - $error->msg; -} - -sub none { - return (); -} - -sub null { - undef; -} - -sub split { - my ($self, $string, $delim, $limit) = @_; - $delim ||= ' '; - $limit ||= -1; - [split $delim, $string, $limit]; -} - -sub sum { - my ($self, @list) = @_; - my $list = ref($list[0]) eq 'ARRAY' ? $list[0] : [@list]; - require List::Util; - List::Util::sum(@$list); -} - -sub text { - my ($self, $list) = @_; - CORE::join "\n", @$list, ''; -} - -sub tojson { - my ($self, $value) = @_; - $self->_json->encode($value); -} - -sub throw { - my ($self, $msg) = @_; - $self->{run}{thrown} = TestMLError->new($msg); - return 0; -} - -sub type { - my ($self, @value) = @_; - return 'none' unless @value; - $self->{run}->type($self->{run}->cook($value[0])); -} - -sub true { - TestML::Boolean::true(); -} - -#------------------------------------------------------------------------------ -my $json; -sub _json { - require JSON::PP; - $json ||= JSON::PP->new - ->pretty - ->indent_length(2) - ->canonical(1) - ->allow_nonref; -} - -#------------------------------------------------------------------------------ -package TestMLError; - -sub new { - my ($class, $msg) = @_; - - return bless { - msg => $msg - }, $class; -} - -sub msg { $_[0]->{msg} } - -1; diff --git a/src/ruby/test/020-pick-exclude.tml b/src/ruby/test/020-pick-exclude.tml new file mode 120000 index 00000000..9765ece8 --- /dev/null +++ b/src/ruby/test/020-pick-exclude.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/020-pick-exclude.tml \ No newline at end of file diff --git a/src/ruby/test/030-assertion-label.tml b/src/ruby/test/030-assertion-label.tml new file mode 120000 index 00000000..c8d79536 --- /dev/null +++ b/src/ruby/test/030-assertion-label.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/030-assertion-label.tml \ No newline at end of file diff --git a/src/ruby/test/040-multiline-point.tml b/src/ruby/test/040-multiline-point.tml new file mode 120000 index 00000000..6a9453cd --- /dev/null +++ b/src/ruby/test/040-multiline-point.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/040-multiline-point.tml \ No newline at end of file diff --git a/src/ruby/test/050-object-type.tml b/src/ruby/test/050-object-type.tml new file mode 120000 index 00000000..f71991a4 --- /dev/null +++ b/src/ruby/test/050-object-type.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/050-object-type.tml \ No newline at end of file diff --git a/src/ruby/test/060-standard-lib.tml b/src/ruby/test/060-standard-lib.tml new file mode 120000 index 00000000..626fae24 --- /dev/null +++ b/src/ruby/test/060-standard-lib.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/060-standard-lib.tml \ No newline at end of file diff --git a/src/ruby/test/070-polymorphic-assertions.tml b/src/ruby/test/070-polymorphic-assertions.tml new file mode 120000 index 00000000..a3df778b --- /dev/null +++ b/src/ruby/test/070-polymorphic-assertions.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/070-polymorphic-assertions.tml \ No newline at end of file diff --git a/src/ruby/test/080-hash-lookup.tml b/src/ruby/test/080-hash-lookup.tml new file mode 120000 index 00000000..168dd4ea --- /dev/null +++ b/src/ruby/test/080-hash-lookup.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/080-hash-lookup.tml \ No newline at end of file diff --git a/src/ruby/test/090-catch-error.tml b/src/ruby/test/090-catch-error.tml new file mode 120000 index 00000000..dec58d21 --- /dev/null +++ b/src/ruby/test/090-catch-error.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/090-catch-error.tml \ No newline at end of file diff --git a/src/ruby/test/100-operator-tests.tml b/src/ruby/test/100-operator-tests.tml new file mode 120000 index 00000000..0ac46c1a --- /dev/null +++ b/src/ruby/test/100-operator-tests.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/100-operator-tests.tml \ No newline at end of file diff --git a/src/ruby/test/110-stdlib-tests.tml b/src/ruby/test/110-stdlib-tests.tml new file mode 120000 index 00000000..e4427741 --- /dev/null +++ b/src/ruby/test/110-stdlib-tests.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/110-stdlib-tests.tml \ No newline at end of file diff --git a/src/ruby/test/120-function-loop.tml b/src/ruby/test/120-function-loop.tml new file mode 120000 index 00000000..12698350 --- /dev/null +++ b/src/ruby/test/120-function-loop.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/120-function-loop.tml \ No newline at end of file diff --git a/src/ruby/test/130-function-assignment.tml b/src/ruby/test/130-function-assignment.tml new file mode 120000 index 00000000..c55965f9 --- /dev/null +++ b/src/ruby/test/130-function-assignment.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/130-function-assignment.tml \ No newline at end of file diff --git a/src/ruby/test/140-pick-func-call.tml b/src/ruby/test/140-pick-func-call.tml new file mode 120000 index 00000000..63cd590a --- /dev/null +++ b/src/ruby/test/140-pick-func-call.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/140-pick-func-call.tml \ No newline at end of file diff --git a/src/ruby/test/150-call-func-with-args.tml b/src/ruby/test/150-call-func-with-args.tml new file mode 120000 index 00000000..bfc4de03 --- /dev/null +++ b/src/ruby/test/150-call-func-with-args.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/150-call-func-with-args.tml \ No newline at end of file diff --git a/src/ruby/test/160-plan.tml b/src/ruby/test/160-plan.tml new file mode 120000 index 00000000..e71fdaca --- /dev/null +++ b/src/ruby/test/160-plan.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/160-plan.tml \ No newline at end of file diff --git a/src/ruby/test/170-block-access.tml b/src/ruby/test/170-block-access.tml new file mode 120000 index 00000000..77f50dc5 --- /dev/null +++ b/src/ruby/test/170-block-access.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/170-block-access.tml \ No newline at end of file diff --git a/src/ruby/test/180-interpolation.tml b/src/ruby/test/180-interpolation.tml new file mode 120000 index 00000000..02f05097 --- /dev/null +++ b/src/ruby/test/180-interpolation.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/180-interpolation.tml \ No newline at end of file diff --git a/src/ruby/test/190-not-assertions.tml b/src/ruby/test/190-not-assertions.tml new file mode 120000 index 00000000..27092cba --- /dev/null +++ b/src/ruby/test/190-not-assertions.tml @@ -0,0 +1 @@ +../../../test/runtime-tml/190-not-assertions.tml \ No newline at end of file