Add test coverage for a bunch of stuff.
This commit is contained in:
parent
3658529772
commit
a7cb693849
44 changed files with 651 additions and 116 deletions
2
Gemfile
2
Gemfile
|
@ -9,3 +9,5 @@ gem 'pry', '~> 0.13.0'
|
|||
gem 'rake', '~> 12.0'
|
||||
gem 'rspec', '~> 3.0'
|
||||
gem 'rubocop', '~> 0.80.1'
|
||||
|
||||
gem 'faker', '~> 2.11'
|
||||
|
|
|
@ -9,8 +9,13 @@ GEM
|
|||
specs:
|
||||
ast (2.4.0)
|
||||
coderay (1.1.2)
|
||||
concurrent-ruby (1.1.6)
|
||||
diff-lcs (1.3)
|
||||
dry-inflector (0.2.0)
|
||||
faker (2.11.0)
|
||||
i18n (>= 1.6, < 2)
|
||||
i18n (1.8.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jaro_winkler (1.5.4)
|
||||
method_source (1.0.0)
|
||||
parallel (1.19.1)
|
||||
|
@ -50,6 +55,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
faker (~> 2.11)
|
||||
pry (~> 0.13.0)
|
||||
rake (~> 12.0)
|
||||
rspec (~> 3.0)
|
||||
|
|
|
@ -15,10 +15,10 @@ module WAG
|
|||
require 'wag/export'
|
||||
require 'wag/f32_instructions'
|
||||
require 'wag/f64_instructions'
|
||||
require 'wag/function_type'
|
||||
require 'wag/function'
|
||||
require 'wag/global'
|
||||
require 'wag/function_type'
|
||||
require 'wag/global_instructions'
|
||||
require 'wag/global'
|
||||
require 'wag/i32_instructions'
|
||||
require 'wag/i64_instructions'
|
||||
require 'wag/import'
|
||||
|
@ -26,6 +26,7 @@ module WAG
|
|||
require 'wag/instruction'
|
||||
require 'wag/label'
|
||||
require 'wag/local_instructions'
|
||||
require 'wag/memory_instructions'
|
||||
require 'wag/memory'
|
||||
require 'wag/module'
|
||||
require 'wag/param'
|
||||
|
|
|
@ -20,14 +20,14 @@ module WAG
|
|||
@desc = Global.new(label, type)
|
||||
end
|
||||
|
||||
def memory(number, min: nil, max: nil, &block)
|
||||
@desc = Memory.new(number, min: min, max: max)
|
||||
def memory(number, min = nil, max = nil, &block)
|
||||
@desc = Memory.new(number, min, max)
|
||||
@desc.instance_exec(&block) if block
|
||||
@desc
|
||||
end
|
||||
|
||||
def table(number, type = :funcref, min: nil, max: nil, &block)
|
||||
@desc = Table.new(number, type, min: min, max: max)
|
||||
def table(number, type = :funcref, &block)
|
||||
@desc = Table.new(number, type)
|
||||
@desc.instance_exec(&block) if block
|
||||
@desc
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ module WAG
|
|||
convert_i64_s convert_i64_u reinterpret_i32].each do |instruction_name|
|
||||
camelised_name = "F32#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
define_method(instruction_name) do |*args, &block|
|
||||
instruction = WAG::Instruction.get_const(camelised_name).new(*args)
|
||||
instruction = WAG::Instruction.const_get(camelised_name).new(*args)
|
||||
@instructions << instruction
|
||||
instruction.instance_exec(&block) if block
|
||||
instruction
|
||||
|
|
|
@ -11,7 +11,7 @@ module WAG
|
|||
convert_i64_s convert_i64_u promote_f32 reinterpret_i64].each do |instruction_name|
|
||||
camelised_name = "F64#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
define_method(instruction_name) do |*args, &block|
|
||||
instruction = WAG::Instruction.get_const(camelised_name).new(*args)
|
||||
instruction = WAG::Instruction.const_get(camelised_name).new(*args)
|
||||
@instructions << instruction
|
||||
instruction.instance_exec(&block) if block
|
||||
instruction
|
||||
|
|
|
@ -7,8 +7,8 @@ module WAG
|
|||
|
||||
attr_reader :label, :params
|
||||
|
||||
def initialize(label)
|
||||
@label = WAG::Label.from(label)
|
||||
def initialize(label = nil)
|
||||
@label = WAG::Label.from(label) if label
|
||||
@params = []
|
||||
end
|
||||
|
||||
|
@ -23,7 +23,8 @@ module WAG
|
|||
end
|
||||
|
||||
def to_sexpr
|
||||
[:func, @label.to_sexpr].tap do |func|
|
||||
[:func].tap do |func|
|
||||
func.push(@label.to_sexpr) if @label
|
||||
func.concat(@params.map(&:to_sexpr)) unless @params.empty?
|
||||
func.push(@result.to_sexpr) if @result
|
||||
end
|
||||
|
|
|
@ -1,31 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WAG
|
||||
class FunctionType
|
||||
include WAG::WAT
|
||||
|
||||
attr_reader :label, :params, :results
|
||||
|
||||
def initialize(label)
|
||||
@label = WAG::Label.from(label)
|
||||
@params = []
|
||||
@results = []
|
||||
end
|
||||
|
||||
def param(*types)
|
||||
@params.concat(types.map { |t| WAG::Type.from(t) })
|
||||
end
|
||||
|
||||
def result(*types)
|
||||
@results.concat(types.map { |t| WAG::Type.from(t) })
|
||||
end
|
||||
|
||||
class FunctionType < Function
|
||||
def to_sexpr
|
||||
[:type, @label.to_sexpr,
|
||||
[:func].tap do |func|
|
||||
func.push([:param].concat(@params.map(&:to_sexpr))) if @params
|
||||
func.push([:result].concat(@results.map(&:to_sexpr))) if @results
|
||||
end]
|
||||
[:type].tap do |type|
|
||||
type.push(@label.to_sexpr) if @label
|
||||
[:func].tap do |func|
|
||||
func.concat(@params.map(&:to_sexpr)) unless @params.empty?
|
||||
func.push(@result.to_sexpr) if @result
|
||||
type.push(func)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,9 +7,9 @@ module WAG
|
|||
end
|
||||
|
||||
%i[get set].each do |instruction_name|
|
||||
camelised_name = "Global#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
camelised_name = "Global::#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
define_method(instruction_name) do |*args, &block|
|
||||
instruction = WAG::Instruction.get_const(camelised_name).new(*args)
|
||||
instruction = WAG::Instruction.const_get(camelised_name).new(*args)
|
||||
@instructions << instruction
|
||||
instruction.instance_exec(&block) if block
|
||||
instruction
|
||||
|
|
|
@ -10,9 +10,9 @@ module WAG
|
|||
ne lt_s lt_u gt_s gt_u le_s le_u ge_s ge_u clz ctz popcnt add sub mul
|
||||
div_s div_u rem_s rem_u and or xor shl shr_s shr_u rotl rotr trunc_f32_s
|
||||
trunc_f32_u trunc_f64_s trunc_f64_u reinterpret_f32].each do |instruction_name|
|
||||
camelised_name = "I32#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
camelised_name = "I32::#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
define_method(instruction_name) do |*args, &block|
|
||||
instruction = WAG::Instruction.get_const(camelised_name).new(*args)
|
||||
instruction = WAG::Instruction.const_get(camelised_name).new(*args)
|
||||
@instructions << instruction
|
||||
instruction.instance_exec(&block) if block
|
||||
instruction
|
||||
|
|
|
@ -9,11 +9,11 @@ module WAG
|
|||
%i[load load8_s load8_u load16_s load16_u load32_s load32_u store store8
|
||||
store16 store32 const eqz eq ne lt_s lt_u gt_s gt_u le_s le_u ge_s ge_u
|
||||
clz ctz popcnt add sub mul div_s div_u rem_s rem_u and or xor shl shr_s
|
||||
shr_u rotl rotr extend_i13_s extend_i32_u trunc_f32_s trunc_f32_u
|
||||
shr_u rotl rotr extend_i32_s extend_i32_u trunc_f32_s trunc_f32_u
|
||||
trunc_f64_s trunc_f64_u reinterpret_f64].each do |instruction_name|
|
||||
camelised_name = "I32#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
camelised_name = "I64::#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
define_method(instruction_name) do |*args, &block|
|
||||
instruction = WAG::Instruction.get_const(camelised_name).new(*args)
|
||||
instruction = WAG::Instruction.const_get(camelised_name).new(*args)
|
||||
@instructions << instruction
|
||||
instruction.instance_exec(&block) if block
|
||||
instruction
|
||||
|
|
|
@ -23,14 +23,14 @@ module WAG
|
|||
@desc
|
||||
end
|
||||
|
||||
def memory(number, min: nil, max: nil, &block)
|
||||
@desc = Memory.new(number, min: min, max: max)
|
||||
def memory(number, min = nil, max = nil, &block)
|
||||
@desc = Memory.new(number, min, max)
|
||||
@desc.instance_exec(&block) if block
|
||||
@desc
|
||||
end
|
||||
|
||||
def table(number, type = :funcref, min: nil, max: nil, &block)
|
||||
@desc = Table.new(number, type, min: min, max: max)
|
||||
def table(number, type = :funcref, &block)
|
||||
@desc = Table.new(number, type)
|
||||
@desc.instance_exec(&block) if block
|
||||
@desc
|
||||
end
|
||||
|
|
|
@ -7,13 +7,21 @@ module WAG
|
|||
call_indirect drop select].each do |instruction_name|
|
||||
camelised_name = WAG::Inflector.inflector.camelize(instruction_name)
|
||||
define_method(instruction_name) do |*args, &block|
|
||||
instruction = WAG::Instruction.get_const(camelised_name).new(*args)
|
||||
instruction = WAG::Instruction.const_get(camelised_name).new(*args)
|
||||
instructions << instruction
|
||||
instruction.instance_exec(&block) if block
|
||||
instruction
|
||||
end
|
||||
end
|
||||
|
||||
def f32
|
||||
WAG::F32Instructions.new(instructions)
|
||||
end
|
||||
|
||||
def f64
|
||||
WAG::F64Instructions.new(instructions)
|
||||
end
|
||||
|
||||
def local
|
||||
WAG::LocalInstructions.new(instructions)
|
||||
end
|
||||
|
|
|
@ -133,6 +133,7 @@ module WAG
|
|||
|
||||
require 'wag/instructions/i64/base'
|
||||
require 'wag/instructions/i64/add'
|
||||
require 'wag/instructions/i64/and'
|
||||
require 'wag/instructions/i64/clz'
|
||||
require 'wag/instructions/i64/const'
|
||||
require 'wag/instructions/i64/ctz'
|
||||
|
|
|
@ -7,7 +7,7 @@ module WAG
|
|||
prepend WAG::Instructable
|
||||
|
||||
def self.instruction(op_code)
|
||||
klass = Class.new(WAG::Instruction::Base)
|
||||
klass = Class.new(self)
|
||||
klass.define_method(:op_code) { op_code }
|
||||
klass
|
||||
end
|
||||
|
|
10
lib/wag/instructions/i64/and.rb
Normal file
10
lib/wag/instructions/i64/and.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WAG
|
||||
module Instruction
|
||||
module I64
|
||||
class And < Base.instruction(0x7c)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,9 +7,9 @@ module WAG
|
|||
end
|
||||
|
||||
%i[get set tee].each do |instruction_name|
|
||||
camelised_name = "Local#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
camelised_name = "Local::#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
define_method(instruction_name) do |*args, &block|
|
||||
instruction = WAG::Instruction.get_const(camelised_name).new(*args)
|
||||
instruction = WAG::Instruction.const_get(camelised_name).new(*args)
|
||||
@instructions << instruction
|
||||
instruction.instance_exec(&block) if block
|
||||
instruction
|
||||
|
|
|
@ -6,19 +6,19 @@ module WAG
|
|||
|
||||
attr_reader :number, :min, :max
|
||||
|
||||
def initialize(number, min: nil, max: nil)
|
||||
def initialize(number, min = nil, max = nil)
|
||||
@number = number
|
||||
@min = min
|
||||
@max = max
|
||||
end
|
||||
|
||||
def to_sexpr
|
||||
wat = [:memory, number]
|
||||
|
||||
wat.concat([:min, min]) if min
|
||||
wat.concat([:max, max]) if max
|
||||
|
||||
wat
|
||||
[:memory, number].tap do |expr|
|
||||
if min
|
||||
expr.push(min)
|
||||
expr.push(max) if max
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,9 +7,9 @@ module WAG
|
|||
end
|
||||
|
||||
%i[size grow].each do |instruction_name|
|
||||
camelised_name = "Memory#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
camelised_name = "Memory::#{WAG::Inflector.inflector.camelize(instruction_name)}"
|
||||
define_method(instruction_name) do |*args, &block|
|
||||
instruction = WAG::Instruction.get_const(camelised_name).new(*args)
|
||||
instruction = WAG::Instruction.const_get(camelised_name).new(*args)
|
||||
@instructions << instruction
|
||||
instruction.instance_exec(&block) if block
|
||||
instruction
|
||||
|
|
|
@ -29,8 +29,8 @@ module WAG
|
|||
export
|
||||
end
|
||||
|
||||
def memory(number, min: nil, max: nil, &block)
|
||||
mem = Memory.new(number, min: min, max: max)
|
||||
def memory(number, min = nil, max = nil, &block)
|
||||
mem = Memory.new(number, min, max)
|
||||
@memories << mem
|
||||
mem.instance_exec(&block) if block
|
||||
mem
|
||||
|
@ -63,7 +63,7 @@ module WAG
|
|||
data
|
||||
end
|
||||
|
||||
def func(label, &block)
|
||||
def func(label = nil, &block)
|
||||
func = Function.new(label)
|
||||
@functions << func
|
||||
func.instance_exec(&block) if block
|
||||
|
|
|
@ -3,27 +3,15 @@
|
|||
module WAG
|
||||
class Table
|
||||
include WAG::WAT
|
||||
attr_reader :elements, :type
|
||||
|
||||
RANGE = (0..0xFFFFFFFF).freeze
|
||||
|
||||
attr_reader :number, :type, :min, :max
|
||||
|
||||
def initialize(number, type = :funcref, min: nil, max: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
||||
raise '`min` must fit in i32 range' if min && !RANGE.cover?(min)
|
||||
raise '`max` must fit in i32 range' if max && !RANGE.cover?(max)
|
||||
raise '`max` must be greater than `min`' if min && max && min > max
|
||||
|
||||
@number = number
|
||||
def initialize(elements, type = :funcref)
|
||||
@elements = elements
|
||||
@type = type
|
||||
@min = min
|
||||
@max = max
|
||||
end
|
||||
|
||||
def to_sexpr
|
||||
expr = [:table, number, type]
|
||||
expr.concat([:min, min]) if min
|
||||
expr.concat([:max, max]) if max
|
||||
expr
|
||||
[:table, elements, type]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
module WAG
|
||||
module Type
|
||||
require 'wag/types/base'
|
||||
require 'wag/types/i32'
|
||||
require 'wag/types/i64'
|
||||
require 'wag/types/f32'
|
||||
require 'wag/types/f64'
|
||||
require 'wag/types/i32'
|
||||
require 'wag/types/i64'
|
||||
|
||||
module_function
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WAG
|
||||
module Type
|
||||
# A WebAssembly function type.
|
||||
class Function < Base
|
||||
attr_reader :parameter_types, :return_types
|
||||
|
||||
def initialize(parameter_types, return_types)
|
||||
parameter_types.map! { |t| Huia::Type.from(t) }
|
||||
return_types.map! { |t| Huia::Type.from(t) }
|
||||
|
||||
raise 'There must only be one return type (at the moment)' unless return_types.one?
|
||||
|
||||
unless parameter_types.all?(&:value?) && return_types.all?(&:value?)
|
||||
raise 'Function type parameters can only be value types'
|
||||
end
|
||||
|
||||
@parameter_types = parameter_types
|
||||
@return_types = return_types
|
||||
end
|
||||
|
||||
def value?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@
|
|||
require 'bundler/setup'
|
||||
require 'wag'
|
||||
require 'pry'
|
||||
require 'faker'
|
||||
|
||||
RSpec.configure do |config|
|
||||
# Enable flags like --only-failures and --next-failure
|
||||
|
|
|
@ -8,23 +8,23 @@ RSpec.describe WAG::Export do
|
|||
|
||||
describe 'When exporting memory' do
|
||||
before do
|
||||
export.memory(1, min: 1, max: 64)
|
||||
export.memory(1, 1, 64)
|
||||
end
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq [:export, 'marty', [:memory, 1, :min, 1, :max, 64]] }
|
||||
it { is_expected.to eq [:export, 'marty', [:memory, 1, 1, 64]] }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'When exporting a table' do
|
||||
before do
|
||||
export.table(1, :funcref, min: 1, max: 64)
|
||||
export.table(1, :funcref)
|
||||
end
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq [:export, 'marty', [:table, 1, :funcref, :min, 1, :max, 64]] }
|
||||
it { is_expected.to eq [:export, 'marty', [:table, 1, :funcref]] }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
80
spec/wag/function_spec.rb
Normal file
80
spec/wag/function_spec.rb
Normal file
|
@ -0,0 +1,80 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Function do
|
||||
let(:label) { Faker::Name.first_name }
|
||||
let(:function) { described_class.new(label) }
|
||||
subject { function }
|
||||
|
||||
describe 'When the function is given a label' do
|
||||
let(:label) { Faker::Name.first_name }
|
||||
|
||||
it 'correctly sets the label' do
|
||||
expect(subject.label.value).to eq label
|
||||
end
|
||||
end
|
||||
|
||||
describe '#param' do
|
||||
let(:type) { :i32 }
|
||||
subject { super().param(label, type) }
|
||||
|
||||
it 'adds the param to the function' do
|
||||
expect { subject }
|
||||
.to change { function.params.size }
|
||||
.from(0)
|
||||
.to(1)
|
||||
end
|
||||
|
||||
it 'correctly sets the param' do
|
||||
subject
|
||||
expect(function.params.first).to be_a(WAG::Param)
|
||||
expect(function.params.first.label.value).to eq(label)
|
||||
expect(function.params.first.type).to be_a(WAG::Type::I32)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#result' do
|
||||
subject { super().result(:i32) }
|
||||
|
||||
it 'correctly sets the result' do
|
||||
subject
|
||||
expect(function.result).to be_a(WAG::Result)
|
||||
expect(function.result.types.size).to eq(1)
|
||||
expect(function.result.types.first).to be_a(WAG::Type::I32)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
|
||||
context 'When the function is empty' do
|
||||
let(:label) { nil }
|
||||
it { is_expected.to eq [:func] }
|
||||
end
|
||||
|
||||
context 'When the function has a label' do
|
||||
it { is_expected.to eq [:func, "$#{label}".to_sym] }
|
||||
|
||||
context 'And the function takes a parameter' do
|
||||
before { function.param(:p0, :i32) }
|
||||
it { is_expected.to eq [:func, "$#{label}".to_sym, %i[param $p0 i32]] }
|
||||
|
||||
context 'And the function takes a second parameter' do
|
||||
before { function.param(:p1, :f32) }
|
||||
it { is_expected.to eq [:func, "$#{label}".to_sym, %i[param $p0 i32], %i[param $p1 f32]] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the function has a return type' do
|
||||
before { function.result(:i64) }
|
||||
it { is_expected.to eq [:func, "$#{label}".to_sym, %i[result i64]] }
|
||||
end
|
||||
|
||||
context 'When the function has a body' do
|
||||
before { function.i64.const(13) }
|
||||
it { is_expected.to eq [:func, "$#{label}".to_sym, [:"i64.const", 13]] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
31
spec/wag/global_instructions_spec.rb
Normal file
31
spec/wag/global_instructions_spec.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::GlobalInstructions do
|
||||
let(:label) { Faker::Name.first_name }
|
||||
let(:instructions) { [] }
|
||||
subject { described_class.new(instructions) }
|
||||
|
||||
%i[get set].each do |instruction_name|
|
||||
describe "##{instruction_name}" do
|
||||
subject { super().public_send(instruction_name, label) }
|
||||
|
||||
it "adds a `global.#{instruction_name}` to the instructions" do
|
||||
expect { subject }
|
||||
.to change { instructions.size }
|
||||
.from(0)
|
||||
.to(1)
|
||||
|
||||
expect(instructions.first)
|
||||
.to be_a(WAG::Instruction::Global.const_get(WAG::Inflector.inflector.camelize(instruction_name)))
|
||||
end
|
||||
|
||||
it 'sets the label correctly' do
|
||||
subject
|
||||
expect(instructions.first.label.value)
|
||||
.to eq(label)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
47
spec/wag/i32_instructions_spec.rb
Normal file
47
spec/wag/i32_instructions_spec.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::I32Instructions do
|
||||
let(:instructions) { [] }
|
||||
subject { described_class.new(instructions) }
|
||||
|
||||
%i[load load8_s load8_u load16_s load16_u store store8 store16 eqz eq
|
||||
ne lt_s lt_u gt_s gt_u le_s le_u ge_s ge_u clz ctz popcnt add sub mul
|
||||
div_s div_u rem_s rem_u and or xor shl shr_s shr_u rotl rotr trunc_f32_s
|
||||
trunc_f32_u trunc_f64_s trunc_f64_u reinterpret_f32].each do |instruction_name|
|
||||
describe "##{instruction_name}" do
|
||||
subject { super().public_send(instruction_name) }
|
||||
|
||||
it "adds a `i32.#{instruction_name}` to the instructions" do
|
||||
expect { subject }
|
||||
.to change { instructions.size }
|
||||
.from(0)
|
||||
.to(1)
|
||||
|
||||
expect(instructions.first)
|
||||
.to be_a(WAG::Instruction::I32.const_get(WAG::Inflector.inflector.camelize(instruction_name)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#const' do
|
||||
let(:value) { rand(0xFFFFFFFF) }
|
||||
subject { super().const(value) }
|
||||
|
||||
it 'adds a `i32.const` to the instructions' do
|
||||
expect { subject }
|
||||
.to change { instructions.size }
|
||||
.from(0)
|
||||
.to(1)
|
||||
|
||||
expect(instructions.first)
|
||||
.to be_a(WAG::Instruction::I32::Const)
|
||||
end
|
||||
|
||||
it 'has the correct value' do
|
||||
subject
|
||||
expect(instructions.first.literal).to eq(value)
|
||||
end
|
||||
end
|
||||
end
|
48
spec/wag/i64_instructions_spec.rb
Normal file
48
spec/wag/i64_instructions_spec.rb
Normal file
|
@ -0,0 +1,48 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::I64Instructions do
|
||||
let(:instructions) { [] }
|
||||
subject { described_class.new(instructions) }
|
||||
|
||||
%i[load load8_s load8_u load16_s load16_u load32_s load32_u store store8
|
||||
store16 store32 eqz eq ne lt_s lt_u gt_s gt_u le_s le_u ge_s ge_u
|
||||
clz ctz popcnt add sub mul div_s div_u rem_s rem_u and or xor shl shr_s
|
||||
shr_u rotl rotr extend_i32_s extend_i32_u trunc_f32_s trunc_f32_u
|
||||
trunc_f64_s trunc_f64_u reinterpret_f64].each do |instruction_name|
|
||||
describe "##{instruction_name}" do
|
||||
subject { super().public_send(instruction_name) }
|
||||
|
||||
it "adds a `i64.#{instruction_name}` to the instructions" do
|
||||
expect { subject }
|
||||
.to change { instructions.size }
|
||||
.from(0)
|
||||
.to(1)
|
||||
|
||||
expect(instructions.first)
|
||||
.to be_a(WAG::Instruction::I64.const_get(WAG::Inflector.inflector.camelize(instruction_name)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#const' do
|
||||
let(:value) { rand(0xFFFFFFFFFFFFFFFF) }
|
||||
subject { super().const(value) }
|
||||
|
||||
it 'adds a `i64.const` to the instructions' do
|
||||
expect { subject }
|
||||
.to change { instructions.size }
|
||||
.from(0)
|
||||
.to(1)
|
||||
|
||||
expect(instructions.first)
|
||||
.to be_a(WAG::Instruction::I64::Const)
|
||||
end
|
||||
|
||||
it 'has the correct value' do
|
||||
subject
|
||||
expect(instructions.first.literal).to eq(value)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -8,23 +8,23 @@ RSpec.describe WAG::Import do
|
|||
|
||||
describe 'When importing memory' do
|
||||
before do
|
||||
import.memory(1, min: 1, max: 64)
|
||||
import.memory(1, 1, 64)
|
||||
end
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq [:import, 'marty', 'mcfly', [:memory, 1, :min, 1, :max, 64]] }
|
||||
it { is_expected.to eq [:import, 'marty', 'mcfly', [:memory, 1, 1, 64]] }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'When importing a table' do
|
||||
before do
|
||||
import.table(1, :funcref, min: 1, max: 64)
|
||||
import.table(1, :funcref)
|
||||
end
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq [:import, 'marty', 'mcfly', [:table, 1, :funcref, :min, 1, :max, 64]] }
|
||||
it { is_expected.to eq [:import, 'marty', 'mcfly', [:table, 1, :funcref]] }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
10
spec/wag/inflector_spec.rb
Normal file
10
spec/wag/inflector_spec.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Inflector do
|
||||
describe '.inflector' do
|
||||
subject { described_class.inflector }
|
||||
it { is_expected.to be_a(Dry::Inflector) }
|
||||
end
|
||||
end
|
59
spec/wag/instructable_spec.rb
Normal file
59
spec/wag/instructable_spec.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Instructable do
|
||||
subject do
|
||||
Class.new do
|
||||
include WAG::Instructable
|
||||
end.new
|
||||
end
|
||||
|
||||
describe '.local' do
|
||||
subject { super().local }
|
||||
it { is_expected.to be_a(WAG::LocalInstructions) }
|
||||
end
|
||||
|
||||
describe '#f32' do
|
||||
subject { super().f32 }
|
||||
it { is_expected.to be_a(WAG::F32Instructions) }
|
||||
end
|
||||
|
||||
describe '#f64' do
|
||||
subject { super().f64 }
|
||||
it { is_expected.to be_a(WAG::F64Instructions) }
|
||||
end
|
||||
|
||||
describe '#i32' do
|
||||
subject { super().i32 }
|
||||
it { is_expected.to be_a(WAG::I32Instructions) }
|
||||
end
|
||||
|
||||
describe '#i64' do
|
||||
subject { super().i64 }
|
||||
it { is_expected.to be_a(WAG::I64Instructions) }
|
||||
end
|
||||
|
||||
describe '#memory' do
|
||||
subject { super().memory }
|
||||
it { is_expected.to be_a(WAG::MemoryInstructions) }
|
||||
end
|
||||
|
||||
%i[unreachable nop block loop if else end br_table return call
|
||||
call_indirect drop select].each do |instruction_name|
|
||||
describe "##{instruction_name}" do
|
||||
subject { super().public_send(instruction_name) }
|
||||
|
||||
it { is_expected.to be_a(WAG::Instruction.const_get(WAG::Inflector.inflector.camelize(instruction_name))) }
|
||||
end
|
||||
end
|
||||
|
||||
%i[br br_if].each do |instruction_name|
|
||||
describe "##{instruction_name}" do
|
||||
let(:label) { Faker::Name.first_name }
|
||||
subject { super().public_send(instruction_name, label) }
|
||||
|
||||
it { is_expected.to be_a(WAG::Instruction.const_get(WAG::Inflector.inflector.camelize(instruction_name))) }
|
||||
end
|
||||
end
|
||||
end
|
31
spec/wag/label_spec.rb
Normal file
31
spec/wag/label_spec.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Label do
|
||||
let(:value) { Faker::Name.first_name }
|
||||
subject { described_class.new(value) }
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq "$#{value}".to_sym }
|
||||
end
|
||||
|
||||
describe '.from' do
|
||||
subject { described_class.from(value) }
|
||||
|
||||
it { is_expected.to be_a(described_class) }
|
||||
|
||||
context 'When the value is a string' do
|
||||
describe '#value' do
|
||||
subject { super().value }
|
||||
it { is_expected.to eq value }
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the value is already a label' do
|
||||
let(:value) { described_class.new(Faker::Name.first_name) }
|
||||
it { is_expected.to eq value }
|
||||
end
|
||||
end
|
||||
end
|
31
spec/wag/local_instructions_spec.rb
Normal file
31
spec/wag/local_instructions_spec.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::LocalInstructions do
|
||||
let(:label) { Faker::Name.first_name }
|
||||
let(:instructions) { [] }
|
||||
subject { described_class.new(instructions) }
|
||||
|
||||
%i[get set tee].each do |instruction_name|
|
||||
describe "##{instruction_name}" do
|
||||
subject { super().public_send(instruction_name, label) }
|
||||
|
||||
it "adds a `local.#{instruction_name}` to the instructions" do
|
||||
expect { subject }
|
||||
.to change { instructions.size }
|
||||
.from(0)
|
||||
.to(1)
|
||||
|
||||
expect(instructions.first)
|
||||
.to be_a(WAG::Instruction::Local.const_get(WAG::Inflector.inflector.camelize(instruction_name)))
|
||||
end
|
||||
|
||||
it 'sets the label correctly' do
|
||||
subject
|
||||
expect(instructions.first.label.value)
|
||||
.to eq(label)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
36
spec/wag/memory_instructions_spec.rb
Normal file
36
spec/wag/memory_instructions_spec.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::MemoryInstructions do
|
||||
let(:instructions) { [] }
|
||||
subject { described_class.new(instructions) }
|
||||
|
||||
describe '#size' do
|
||||
subject { super().size }
|
||||
|
||||
it 'adds a `memory.size` to the instructions' do
|
||||
expect { subject }
|
||||
.to change { instructions.size }
|
||||
.from(0)
|
||||
.to(1)
|
||||
|
||||
expect(instructions.first)
|
||||
.to be_a(WAG::Instruction::Memory::Size)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#grow' do
|
||||
subject { super().grow }
|
||||
|
||||
it 'adds a `memory.grow` to the instructions' do
|
||||
expect { subject }
|
||||
.to change { instructions.size }
|
||||
.from(0)
|
||||
.to(1)
|
||||
|
||||
expect(instructions.first)
|
||||
.to be_a(WAG::Instruction::Memory::Grow)
|
||||
end
|
||||
end
|
||||
end
|
28
spec/wag/memory_spec.rb
Normal file
28
spec/wag/memory_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Memory do
|
||||
let(:number) { rand(99) }
|
||||
let(:min) { nil }
|
||||
let(:max) { nil }
|
||||
subject { described_class.new(number, min, max) }
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
|
||||
describe 'When there is no minumum value' do
|
||||
it { is_expected.to eq [:memory, number] }
|
||||
end
|
||||
|
||||
describe 'When there is a minumum value' do
|
||||
let(:min) { rand(99) }
|
||||
it { is_expected.to eq [:memory, number, min] }
|
||||
|
||||
describe 'When there is a max value' do
|
||||
let(:max) { min + rand(99) }
|
||||
it { is_expected.to eq [:memory, number, min, max] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
22
spec/wag/param_spec.rb
Normal file
22
spec/wag/param_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Param do
|
||||
let(:type) { :i32 }
|
||||
subject { described_class.new(type, label) }
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
|
||||
context 'When the param has a label' do
|
||||
let(:label) { :foo }
|
||||
it { is_expected.to eq %i[param $foo i32] }
|
||||
end
|
||||
|
||||
context 'When the param has no label' do
|
||||
let(:label) { nil }
|
||||
it { is_expected.to eq %i[param i32] }
|
||||
end
|
||||
end
|
||||
end
|
13
spec/wag/result_spec.rb
Normal file
13
spec/wag/result_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Result do
|
||||
let(:types) { %i[i32 i64] }
|
||||
subject { described_class.new(*types) }
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq %i[result i32 i64] }
|
||||
end
|
||||
end
|
13
spec/wag/table_spec.rb
Normal file
13
spec/wag/table_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Table do
|
||||
let(:elements) { rand(99) }
|
||||
subject { described_class.new(elements) }
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq [:table, elements, :funcref] }
|
||||
end
|
||||
end
|
63
spec/wag/type_spec.rb
Normal file
63
spec/wag/type_spec.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Type do
|
||||
describe '.from' do
|
||||
subject { described_class.from(argument) }
|
||||
|
||||
context 'When the argument is `:f32`' do
|
||||
let(:argument) { :f32 }
|
||||
it { is_expected.to be_a(WAG::Type::F32) }
|
||||
end
|
||||
|
||||
context 'When the argument is `:f64`' do
|
||||
let(:argument) { :f64 }
|
||||
it { is_expected.to be_a(WAG::Type::F64) }
|
||||
end
|
||||
|
||||
context 'When the argument is `:i32`' do
|
||||
let(:argument) { :i32 }
|
||||
it { is_expected.to be_a(WAG::Type::I32) }
|
||||
end
|
||||
|
||||
context 'When the argument is `:i64`' do
|
||||
let(:argument) { :i64 }
|
||||
it { is_expected.to be_a(WAG::Type::I64) }
|
||||
end
|
||||
|
||||
context 'When the argument is a `WAG::Type::F32`' do
|
||||
let(:argument) { WAG::Type::F32.new }
|
||||
it { is_expected.to eq argument }
|
||||
end
|
||||
|
||||
context 'When the argument is a `WAG::Type::F64`' do
|
||||
let(:argument) { WAG::Type::F64.new }
|
||||
it { is_expected.to eq argument }
|
||||
end
|
||||
|
||||
context 'When the argument is a `WAG::Type::I32`' do
|
||||
let(:argument) { WAG::Type::I32.new }
|
||||
it { is_expected.to eq argument }
|
||||
end
|
||||
|
||||
context 'When the argument is a `WAG::Type::I64`' do
|
||||
let(:argument) { WAG::Type::I64.new }
|
||||
it { is_expected.to eq argument }
|
||||
end
|
||||
|
||||
context 'When the argument is an unknown symbol' do
|
||||
let(:argument) { :marty_mcfly }
|
||||
it 'raises a NameError' do
|
||||
expect { subject }.to raise_error(NameError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the argument is an unknown object' do
|
||||
let(:argument) { WAG::Instruction::Nop.new }
|
||||
it 'raises a NameError' do
|
||||
expect { subject }.to raise_error(NameError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
12
spec/wag/types/f32_spec.rb
Normal file
12
spec/wag/types/f32_spec.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Type::F32 do
|
||||
it { is_expected.to be_a(WAG::Type::Base) }
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq :f32 }
|
||||
end
|
||||
end
|
12
spec/wag/types/f64_spec.rb
Normal file
12
spec/wag/types/f64_spec.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Type::F64 do
|
||||
it { is_expected.to be_a(WAG::Type::Base) }
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq :f64 }
|
||||
end
|
||||
end
|
12
spec/wag/types/i32_spec.rb
Normal file
12
spec/wag/types/i32_spec.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Type::I32 do
|
||||
it { is_expected.to be_a(WAG::Type::Base) }
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq :i32 }
|
||||
end
|
||||
end
|
12
spec/wag/types/i64_spec.rb
Normal file
12
spec/wag/types/i64_spec.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WAG::Type::I64 do
|
||||
it { is_expected.to be_a(WAG::Type::Base) }
|
||||
|
||||
describe '#to_sexpr' do
|
||||
subject { super().to_sexpr }
|
||||
it { is_expected.to eq :i64 }
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue