1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
require "spec"
require "../support/tempfile"
require "../support/fibers"
require "../support/win32"
def datapath(*components)
File.join("spec", "std", "data", *components)
end
private class Witness
@checked = false
def check
@checked = true
end
def checked?
@checked
end
end
def spawn_and_wait(before : Proc(_), file = __FILE__, line = __LINE__, &block)
spawn_and_check(before, file, line) do |w|
block.call
w.check
end
end
def spawn_and_check(before : Proc(_), file = __FILE__, line = __LINE__, &block : Witness -> _)
done = Channel(Exception?).new
w = Witness.new
# State of the "before" filter:
# 0 - not started
# 1 - started
# 2 - completed
x = Atomic(Int32).new(0)
before_fiber = spawn do
x.set(1)
# This is a workaround to ensure the "before" fiber
# is unscheduled. Otherwise it might stay alive running the event loop
spawn(same_thread: true) do
while x.get != 2
Fiber.yield
end
end
before.call
x.set(2)
end
spawn do
begin
# Wait until the "before" fiber starts
while x.get == 0
Fiber.yield
end
# Now wait until the "before" fiber is blocked
wait_until_blocked before_fiber
block.call w
done.send nil
rescue e
done.send e
end
end
ex = done.receive
raise ex if ex
unless w.checked?
fail "Failed to stress expected path", file, line
end
end
def compile_file(source_file, *, bin_name = "executable_file", flags = %w(), file = __FILE__)
with_temp_executable(bin_name, file: file) do |executable_file|
compiler = ENV["CRYSTAL_SPEC_COMPILER_BIN"]? || "bin/crystal"
Process.run(compiler, ["build"] + flags + ["-o", executable_file, source_file], env: {
"CRYSTAL_PATH" => Crystal::PATH,
"CRYSTAL_LIBRARY_PATH" => Crystal::LIBRARY_PATH,
"CRYSTAL_CACHE_DIR" => Crystal::CACHE_DIR,
}, error: Process::Redirect::Inherit)
File.exists?(executable_file).should be_true
yield executable_file
end
end
def compile_source(source, flags = %w(), file = __FILE__)
with_tempfile("source_file", file: file) do |source_file|
File.write(source_file, source)
compile_file(source_file, flags: flags, file: file) do |executable_file|
yield executable_file
end
end
end
def compile_and_run_file(source_file, flags = %w(), file = __FILE__)
compile_file(source_file, flags: flags, file: file) do |executable_file|
output, error = IO::Memory.new, IO::Memory.new
status = Process.run executable_file, output: output, error: error
{status, output.to_s, error.to_s}
end
end
def compile_and_run_source(source, flags = %w(), file = __FILE__)
with_tempfile("source_file", file: file) do |source_file|
File.write(source_file, source)
compile_and_run_file(source_file, flags, file: file)
end
end
def compile_and_run_source_with_c(c_code, crystal_code, flags = %w(--debug), file = __FILE__)
with_temp_c_object_file(c_code, file: file) do |o_filename|
yield compile_and_run_source(%(
require "prelude"
@[Link(ldflags: #{o_filename.inspect})]
#{crystal_code}
))
end
end
|