Permalink
Browse files

Replace StackBusters with TCO assertions (mbittarelli FTW)

  • Loading branch information...
tdg5 committed Mar 18, 2015
1 parent e2e7f30 commit c2963276376f7705b2fb1b6b582d88f07954c02f
View
@@ -12,9 +12,7 @@
require "mocha/setup"
require "tco_method"
require "test_helpers/vm_stack_helper"
require "test_helpers/factorial_stack_buster_helper"
require "test_helpers/vanilla_stack_buster_helper"
require_relative "test_helpers/assertions"
# Use alternate shoulda-style DSL for tests
class TCOMethod::TestCase < Minitest::Spec
@@ -0,0 +1,28 @@
module TCOMethod
module TestHelpers
module Assertions
def assert_tail_call_optimized(method, *args)
is_tco = tail_call_optimized(method, *args)
msg = "Expected method #{method.name} to be tail call optimized"
assert is_tco, msg
end
def refute_tail_call_optimized(method, *args)
is_tco = tail_call_optimized(method, *args)
msg = "Expected method #{method.name} not to be tail call optimized"
refute is_tco, msg
end
def tail_call_optimized?(method, *args)
initial_length = nil
method.call(*args) do
if initial_length.nil?
initial_length = caller.length
else
break initial_length == caller.length
end
end
end
end
end
end

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.
View
@@ -1,8 +1,6 @@
require "test_helper"
class MixinTest < TCOMethod::TestCase
include TCOMethod::TestHelpers::FactorialStackBusterHelper
TestClass = Class.new { extend TCOMethod::Mixin }
TestModule = Module.new { extend TCOMethod::Mixin }
@@ -1,7 +1,7 @@
require "test_helper"
class TCOMethodTest < TCOMethod::TestCase
include TCOMethod::TestHelpers::FactorialStackBusterHelper
include TCOMethod::TestHelpers::Assertions
Subject = TCOMethod
@@ -14,26 +14,34 @@ class << self
# Equivalent to the below, but provides a target for verifying that
# tco_module_method works on Classes and tco_class_method works on Modules.
def self.module_factorial(n, acc = 1)
n <= 1 ? acc : module_factorial(n - 1, n * acc)
def self.module_fib_yielder(index, back_one = 1, back_two = 0, &block)
yield back_two if index > 0
index < 1 ? back_two : module_fib_yielder(index - 1, back_one + back_two, back_one, &block)
end
# Equivalent to the above, but provides a target for verifying that
# tco_module_method works on Classes and tco_class_method works on Modules.
def self.class_factorial(n, acc = 1)
n <= 1 ? acc : class_factorial(n - 1, n * acc)
def self.class_fib_yielder(index, back_one = 1, back_two = 0, &block)
yield back_two if index > 0
index < 1 ? back_two : class_fib_yielder(index - 1, back_one + back_two, back_one, &block)
end
define_method(:instance_block_method) { }
def instance_factorial(n, acc = 1)
n <= 1 ? acc : instance_factorial(n - 1, n * acc)
# Equivalent to the above, but provides a target for verifying that
# instance methods work for both Classes and Modules
def instance_fib_yielder(index, back_one = 1, back_two = 0, &block)
yield back_two if index > 0
index < 1 ? back_two : instance_fib_yielder(index - 1, back_one + back_two, back_one, &block)
end
end
TestModule = Module.new(&test_subject_builder)
TestClass = Class.new(&test_subject_builder)
# Grab source before it's recompiled for use later
InstanceFibYielderSource = TestClass.instance_method(:instance_fib_yielder).source
subject { Subject }
context Subject.name do
@@ -57,21 +65,15 @@ def instance_factorial(n, acc = 1)
end
should "compile the given code with tail call optimization" do
FactorialEvalDummy = dummy_class = Class.new
EvalDummy = dummy_class = Class.new
subject.tco_eval(<<-CODE)
class #{dummy_class.name}
def factorial(n, acc = 1)
n <= 1 ? acc : factorial(n - 1, n * acc)
end
#{InstanceFibYielderSource}
end
CODE
# Exceed maximum available stack depth by 100 for good measure
factorial_seed = factorial_stack_buster_stack_depth_remaining + 100
assert_unoptimized_factorial_stack_overflow(factorial_seed)
expected_result = iterative_factorial(factorial_seed)
assert_equal expected_result, dummy_class.new.factorial(factorial_seed)
fib_yielder = dummy_class.new.method(:instance_fib_yielder)
assert tail_call_optimized?(fib_yielder, 5)
end
end
@@ -119,15 +121,12 @@ def factorial(n, acc = 1)
end
should "re-compile the given method with tail call optimization" do
# Exceed maximum available stack depth by 100 for good measure
factorial_seed = factorial_stack_buster_stack_depth_remaining + 100
assert_raises(SystemStackError) do
method_owner.module_factorial(factorial_seed)
end
fib_yielder = method_owner.method(:module_fib_yielder)
refute tail_call_optimized?(fib_yielder, 5)
subject.call(method_owner, :module_factorial, :module)
expected_result = iterative_factorial(factorial_seed)
assert_equal expected_result, method_owner.module_factorial(factorial_seed)
subject.call(method_owner, :module_fib_yielder, :module)
fib_yielder = method_owner.method(:module_fib_yielder)
assert tail_call_optimized?(fib_yielder, 5)
end
end
@@ -139,15 +138,12 @@ def factorial(n, acc = 1)
end
should "re-compile the given method with tail call optimization" do
# Exceed maximum available stack depth by 100 for good measure
factorial_seed = factorial_stack_buster_stack_depth_remaining + 100
assert_raises(SystemStackError) do
method_owner.class_factorial(factorial_seed)
end
fib_yielder = method_owner.method(:class_fib_yielder)
refute tail_call_optimized?(fib_yielder, 5)
subject.call(method_owner, :class_factorial, method_owner_class)
expected_result = iterative_factorial(factorial_seed)
assert_equal expected_result, method_owner.class_factorial(factorial_seed)
subject.call(method_owner, :class_fib_yielder, :module)
fib_yielder = method_owner.method(:class_fib_yielder)
assert tail_call_optimized?(fib_yielder, 5)
end
end
@@ -159,16 +155,14 @@ def factorial(n, acc = 1)
end
should "re-compile the given method with tail call optimization" do
# Exceed maximum available stack depth by 100 for good measure
factorial_seed = factorial_stack_buster_stack_depth_remaining + 100
instance_class = instance_class_for_receiver(method_owner)
assert_raises(SystemStackError) do
instance_class.new.instance_factorial(factorial_seed)
end
subject.call(method_owner, :instance_factorial, :instance)
expected_result = iterative_factorial(factorial_seed)
assert_equal expected_result, instance_class.new.instance_factorial(factorial_seed)
fib_yielder = instance_class.new.method(:instance_fib_yielder)
refute tail_call_optimized?(fib_yielder, 5)
subject.call(method_owner, :instance_fib_yielder, :instance)
fib_yielder = instance_class.new.method(:instance_fib_yielder)
assert tail_call_optimized?(fib_yielder, 5)
end
end
end

0 comments on commit c296327

Please sign in to comment.