diff --git a/.gitignore b/.gitignore index 8d8c1f28..f6710f50 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,6 @@ zigler-*.tar # erlang module zig files .test_*.zig # other compiled files -*.a \ No newline at end of file +*.a + +/test/code \ No newline at end of file diff --git a/lib/zig/_nif.ex b/lib/zig/_nif.ex index 89c903a8..f3decd84 100644 --- a/lib/zig/_nif.ex +++ b/lib/zig/_nif.ex @@ -13,7 +13,7 @@ defmodule Zig.Nif do @enforce_keys ~w[name export concurrency file module_code_path zig_code_path]a - defstruct @enforce_keys ++ ~w[line signature params return leak_check alias doc spec raw]a + defstruct @enforce_keys ++ ~w[line signature params return leak_check alias doc raw]a alias Zig.Nif.Concurrency alias Zig.Nif.DirtyCpu @@ -41,7 +41,6 @@ defmodule Zig.Nif do leak_check: boolean(), alias: nil | atom, doc: nil | String.t(), - spec: Macro.t(), raw: :term | :erl_nif_term } @@ -59,7 +58,6 @@ defmodule Zig.Nif do leak_check: boolean(), alias: nil | atom, doc: nil | String.t(), - spec: Macro.t(), raw: nil } @@ -77,7 +75,6 @@ defmodule Zig.Nif do | {:return, Return.opts()} | {:alias, atom()} | {:doc, String.t()} - | {:spec, Macro.t()} @type opts() :: [defaultable_opts | individual_opts] @@ -127,32 +124,10 @@ defmodule Zig.Nif do end end - typespec = - case nif.spec do - false -> - quote do - end - - _ -> - nif.spec - |> List.wrap() - |> Enum.map(fn spec -> - nil - end) - - {_, list} when is_list(list) -> - Enum.map(list, fn spec -> - quote do - @spec unquote(spec) - end - end) - end - functions = concurrency.render_elixir(nif) quote context: Elixir do unquote(doc) - unquote(typespec) unquote(functions) end end diff --git a/lib/zig/compile_error.ex b/lib/zig/compile_error.ex index 5d4c008c..459ceb20 100644 --- a/lib/zig/compile_error.ex +++ b/lib/zig/compile_error.ex @@ -12,8 +12,14 @@ defmodule Zig.CompileError do |> Enum.reduce({[], nil}, fn error_line, {so_far, nil} -> {maybe_line, fileline} = revise_line(error_line, so_far, zig_code_path, manifest_module) - [_, _ | rest] = List.flatten(maybe_line) - {[IO.iodata_to_binary(rest)], fileline} + + case List.flatten(maybe_line) do + [str1, str2 | rest] when is_binary(str1) and is_binary(str2) -> + {[IO.iodata_to_binary(rest)], fileline} + + _ -> + {maybe_line, fileline} + end error_line, {so_far, fileline} -> {next, _} = revise_line(error_line, so_far, zig_code_path, manifest_module) diff --git a/mix.exs b/mix.exs index d1f129a7..4dcde8f3 100644 --- a/mix.exs +++ b/mix.exs @@ -26,7 +26,11 @@ defmodule Zigler.MixProject do preferred_cli_env: [dialyzer: :dev], source_url: "https://github.com/E-xyza/zigler/", docs: docs(), - aliases: [docs: "zig_doc"] + aliases: [docs: "zig_doc"], + test_elixirc_options: [ + debug_info: true, + docs: true + ] ] end diff --git a/test/_support/compiler.ex b/test/_support/compiler.ex new file mode 100644 index 00000000..eb01f9c4 --- /dev/null +++ b/test/_support/compiler.ex @@ -0,0 +1,27 @@ +defmodule ZiglerTest.Compiler do + + @root_dir "test/code" + + def init do + if File.dir?(@root_dir) do + File.rm_rf!(@root_dir) + end + + File.mkdir_p!(@root_dir) + + :code.add_pathz(~c'#{@root_dir}') + end + + defmacro compile(file) do + quote do + [{mod, bin}] = + __DIR__ + |> Path.join(unquote(file)) + |> Code.compile_file() + + beamfile = Path.join(unquote(@root_dir), "#{mod}.beam") + + File.write!(beamfile, bin) + end + end +end \ No newline at end of file diff --git a/test/_support/tests/documentation.ex b/test/_support/tests/documentation.ex deleted file mode 100644 index 0eddd3a1..00000000 --- a/test/_support/tests/documentation.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule ZiglerTest.Documentation do - @moduledoc false - use Zig, otp_app: :zigler, nifs: [..., no_docs: [docs: false]] - - ~Z""" - const beam = @import("beam"); - - /// This is a function that does something. - pub fn do_something(term: beam.term) beam.term { - const value = beam.get(i32, term, .{}) catch unreachable; - return beam.make(value + 47, .{}); - } - - /// This function has its docs suppressed - pub fn no_docs(term: beam.term) beam.term { - return term; - } - """ -end diff --git a/test/_support/tests/override_typespec.ex b/test/integration/_documentation.ex similarity index 58% rename from test/_support/tests/override_typespec.ex rename to test/integration/_documentation.ex index 35244081..b78730a4 100644 --- a/test/_support/tests/override_typespec.ex +++ b/test/integration/_documentation.ex @@ -1,12 +1,12 @@ -defmodule ZiglerTest.OverrideTypespec do - @moduledoc false - use Zig, otp_app: :zigler, nifs: [do_something: [spec: (integer -> integer)]] +defmodule ZiglerTest.Documentation do + use Zig, otp_app: :zigler ~Z""" const beam = @import("beam"); + /// This is a function that does something pub fn do_something(term: beam.term) beam.term { const value = beam.get(i32, term, .{}) catch unreachable; return beam.make(value + 47, .{}); } """ -end +end \ No newline at end of file diff --git a/test/integration/_typespec_override.ex b/test/integration/_typespec_override.ex new file mode 100644 index 00000000..67f98c2b --- /dev/null +++ b/test/integration/_typespec_override.ex @@ -0,0 +1,15 @@ +defmodule ZiglerTest.TypespecOverride do + @compile :debug_info + + use Zig, otp_app: :zigler, nifs: [do_something: [specs: false]] + + @spec do_something(integer) :: integer + + ~Z""" + const beam = @import("beam"); + pub fn do_something(term: beam.term) beam.term { + const value = beam.get(i32, term, .{}) catch unreachable; + return beam.make(value + 47, .{}); + } + """ +end diff --git a/test/integration/callbacks/on_load_automatic_get_test.exs b/test/integration/callbacks/on_load_automatic_get_test.exs new file mode 100644 index 00000000..3bfa4a43 --- /dev/null +++ b/test/integration/callbacks/on_load_automatic_get_test.exs @@ -0,0 +1,39 @@ +defmodule ZiglerTest.Callbacks.OnLoadAutomaticGetTest do + # this is a test of the "automatic" on_load function. This means that the + # beam.context.env variable is set, and the term value is typed. + # the return value is also allowed to be an enum value + # + # the magic __on_load__ function is also tested here. + + use ZiglerTest.IntegrationCase, async: true + + use Zig, otp_app: :zigler, callbacks: [on_load: :automatic] + + ~Z""" + const beam = @import("beam"); + + var stored_mode: beam.ContextMode = undefined; + var stored_number: u32 = undefined; + + pub fn automatic(_: [*c]?*anyopaque, number: u32) void { + stored_mode = beam.context.mode; + stored_number = number; + } + + pub fn success() beam.term { + return beam.make(.{stored_mode, beam.context.mode, stored_number}, .{}); + } + """ + + defp __on_load__, do: 47 + + test "on_load can use automatic mode" do + assert {:callback, :synchronous, 47} = success() + end + + test "the on_load function is not exported" do + refute :functions + |> __MODULE__.__info__() + |> Keyword.has_key?(:automatic) + end +end diff --git a/test/integration/callbacks/on_load_erroring_enum_test.exs b/test/integration/callbacks/on_load_erroring_enum_test.exs index b31ab060..42e1555b 100644 --- a/test/integration/callbacks/on_load_erroring_enum_test.exs +++ b/test/integration/callbacks/on_load_erroring_enum_test.exs @@ -4,7 +4,7 @@ defmodule ZiglerTest.Callbacks.OnLoadErroringEnumTest do use ZiglerTest.IntegrationCase, async: true import ExUnit.CaptureLog - test "compiler error when on_load function errors out" do + test "error when on_load function errors out" do log = capture_log(fn -> Code.compile_quoted( diff --git a/test/integration/callbacks/on_load_get_error_test.exs b/test/integration/callbacks/on_load_get_error_test.exs new file mode 100644 index 00000000..5254002c --- /dev/null +++ b/test/integration/callbacks/on_load_get_error_test.exs @@ -0,0 +1,30 @@ +defmodule ZiglerTest.Callbacks.OnLoadGetErrorTest do + # this is a test of the "automatic" on_load function. + + use ZiglerTest.IntegrationCase, async: true + import ExUnit.CaptureLog + + test "error when on_load function is passed the wrong type" do + log = + capture_log(fn -> + Code.compile_quoted( + quote do + defmodule ZiglerTest.OnLoadGetError do + use Zig, otp_app: :zigler, callbacks: [on_load: :foo], dir: unquote(__DIR__) + + def __on_load__, do: "not_an_integer" + + ~Z""" + const beam = @import("beam"); + pub fn foo(_: [*c]?*anyopaque, _: i32) void { + } + """ + end + end + ) + end) + + assert log =~ "[error] loading module Elixir.ZiglerTest.OnLoadGetError" + assert log =~ "(42)" + end +end diff --git a/test/integration/callbacks/on_load_malformed_test.exs b/test/integration/callbacks/on_load_malformed_test.exs index 469d69b8..a35c6fe3 100644 --- a/test/integration/callbacks/on_load_malformed_test.exs +++ b/test/integration/callbacks/on_load_malformed_test.exs @@ -72,6 +72,10 @@ defmodule ZiglerTest.Callbacks.OnLoadMalformedTest do ) end end + + test "uses a non-pub struct" + + test "uses a type that isn't approved by beam.get" end describe "compiler error when on_load arity 3" do diff --git a/test/integration/callbacks/on_upgrade_automatic_get_test.exs b/test/integration/callbacks/on_upgrade_automatic_get_test.exs new file mode 100644 index 00000000..b76ecd19 --- /dev/null +++ b/test/integration/callbacks/on_upgrade_automatic_get_test.exs @@ -0,0 +1,47 @@ +defmodule ZiglerTest.Callbacks.OnUpgradeAutomaticGetTest do + # this is a test of the "automatic" on_upgrade function. This means that the + # beam.context.env variable is set, and the term value is set to beam.term. + # the return value is also allowed to be a void value + + use ZiglerTest.IntegrationCase, async: true + + import ExUnit.CaptureIO + + def build_module(opts) do + Code.compile_quoted( + quote do + defmodule ZiglerTest.OnUpgradeAutomaticGet do + use Zig, otp_app: :zigler, callbacks: [on_upgrade: :on_upgrade], dir: unquote(__DIR__) + + defp __on_load__, do: unquote(opts) + + ~z""" + const beam = @import("beam"); + const S = struct{ pid: beam.pid, value: i32}; + + pub fn on_upgrade(_: [*c]?*anyopaque, _: [*c]?*anyopaque, config: S) void { + _ = beam.send(config.pid, .{.result, config.value}, .{}) catch unreachable; + } + pub fn bar() u8 { return #{unquote(opts[:value])}; } + """ + end + end + ) + end + + alias ZiglerTest.OnUpgradeAutomaticGet + + test "on_upgrade generally works" do + this = self() + build_module(pid: this, value: 0) + assert 0 = apply(OnUpgradeAutomaticGet, :bar, []) + + redefine_warn = + capture_io(:stderr, fn -> + build_module(pid: self(), value: 42) + assert_receive {:result, 42} + end) + + assert redefine_warn =~ "redefining module ZiglerTest.OnUpgradeAutomaticGet" + end +end diff --git a/test/integration/callbacks/on_upgrade_malformed_test.exs b/test/integration/callbacks/on_upgrade_malformed_test.exs index 57056585..21a334a4 100644 --- a/test/integration/callbacks/on_upgrade_malformed_test.exs +++ b/test/integration/callbacks/on_upgrade_malformed_test.exs @@ -72,6 +72,10 @@ defmodule ZiglerTest.Callbacks.OnUpgradeMalformedTest do ) end end + + test "uses a an non-pub struct" + + test "uses an unapproved type" end describe "compiler error when on_upgrade arity 4" do diff --git a/test/integration/doc/doc_comment_test.exs b/test/integration/documentation_test.exs similarity index 71% rename from test/integration/doc/doc_comment_test.exs rename to test/integration/documentation_test.exs index 882c5823..772898c2 100644 --- a/test/integration/doc/doc_comment_test.exs +++ b/test/integration/documentation_test.exs @@ -1,6 +1,14 @@ -defmodule ZiglerTest.Unit.Typespec.DocCommentTest do +defmodule ZiglerTest.Unit.Typespec.Documentation do use ExUnit.Case, async: true + @moduletag :documentation + + require ZiglerTest.Compiler + + setup_all do + ZiglerTest.Compiler.compile("_documentation.ex") + end + test "it is possible to write a doc comment" do assert {:docs_v1, _, _, _, _, _, funcs} = Code.fetch_docs(ZiglerTest.Documentation) diff --git a/test/integration/raw/_error_raw_must_have_arity_test.exs b/test/integration/raw/_error_raw_must_have_arity_test.exs index e69de29b..bad132c1 100644 --- a/test/integration/raw/_error_raw_must_have_arity_test.exs +++ b/test/integration/raw/_error_raw_must_have_arity_test.exs @@ -0,0 +1,5 @@ +defmodule ZiglerTest.Integration.Raw.ErrorRawMustHaveArityTest do + use ExUnit.Case, async: true + @tag :skip + test "restore" +end \ No newline at end of file diff --git a/test/integration/raw/_error_raw_must_have_correct_params_test.exs b/test/integration/raw/_error_raw_must_have_correct_params_test.exs index e69de29b..0088fabf 100644 --- a/test/integration/raw/_error_raw_must_have_correct_params_test.exs +++ b/test/integration/raw/_error_raw_must_have_correct_params_test.exs @@ -0,0 +1,5 @@ +defmodule ZiglerTest.Integration.Raw.ErrorRawMustHaveCorrectParamsTest do + use ExUnit.Case, async: true + @tag :skip + test "restore" +end \ No newline at end of file diff --git a/test/integration/raw/_typespec.ex b/test/integration/raw/_typespec.ex new file mode 100644 index 00000000..80f10f3d --- /dev/null +++ b/test/integration/raw/_typespec.ex @@ -0,0 +1,11 @@ +defmodule ZiglerTest.Integration.Raw.Typespec do + @compile :debug_info + use Zig, otp_app: :zigler, nifs: [raw: [arity: 1]] + + ~Z""" + const beam = @import("beam"); + pub fn raw(_: beam.env, _: c_int, terms: [*]const beam.term) beam.term { + return terms[0]; + } + """ +end diff --git a/test/integration/raw/typespec_test.exs b/test/integration/raw/typespec_test.exs new file mode 100644 index 00000000..88d57ad8 --- /dev/null +++ b/test/integration/raw/typespec_test.exs @@ -0,0 +1,25 @@ +defmodule ZiglerTest.Unit.Typespec.RawTest do + use ExUnit.Case, async: true + + @moduletag :typespec + + require ZiglerTest.Compiler + + setup_all do + ZiglerTest.Compiler.compile("_typespec.ex") + :ok + end + + test "raw call with single arity has correct typespecs" do + assert {:ok, [ + {{:raw, _}, + [ + {:type, _, :fun, + [ + {:type, _, :product, [{:type, _, :term, _}]}, + {:type, _, :term, _} + ]} + ]} + ]} = Code.Typespec.fetch_specs(ZiglerTest.Integration.Raw.Typespec) + end +end diff --git a/test/unit/sigil_z/multi_test.exs b/test/integration/sigil_z/multi_test.exs similarity index 100% rename from test/unit/sigil_z/multi_test.exs rename to test/integration/sigil_z/multi_test.exs diff --git a/test/unit/sigil_z/one_test.exs b/test/integration/sigil_z/one_test.exs similarity index 100% rename from test/unit/sigil_z/one_test.exs rename to test/integration/sigil_z/one_test.exs diff --git a/test/integration/types/other_test.exs b/test/integration/types/other_test.exs index dccb7729..60befc65 100644 --- a/test/integration/types/other_test.exs +++ b/test/integration/types/other_test.exs @@ -4,7 +4,7 @@ defmodule ZiglerTest.Types.OtherTest do describe "beam.env" do test "cannot generally be used as a parameter" do assert_raise CompileError, - "test/integration/types/errors/beam_env_fails.exs:6: functions cannot have beam.env as a parameter", + "test/integration/types/errors/beam_env_fails.exs:6: nif function `forbidden` cannot have a value of type beam.env as a parameter", fn -> Code.compile_file("errors/beam_env_fails.exs", __DIR__) end diff --git a/test/integration/typespec/override_test.exs b/test/integration/typespec_override_test.exs similarity index 68% rename from test/integration/typespec/override_test.exs rename to test/integration/typespec_override_test.exs index 62b706c7..1e0008a5 100644 --- a/test/integration/typespec/override_test.exs +++ b/test/integration/typespec_override_test.exs @@ -1,7 +1,13 @@ defmodule ZiglerTest.Integration.Typespec.OverrideTest do use ExUnit.Case, async: true - # NB: the code for this function is test/_support/tests + @moduletag :typespec + + require ZiglerTest.Compiler + + setup_all do + ZiglerTest.Compiler.compile("_typespec_override.ex") + end test "it is possible to override the typespec" do assert {:ok, @@ -14,6 +20,6 @@ defmodule ZiglerTest.Integration.Typespec.OverrideTest do {:type, _, :integer, _} ]} ]} - ]} = Code.Typespec.fetch_specs(ZiglerTest.OverrideTypespec) + ]} = Code.Typespec.fetch_specs(ZiglerTest.TypespecOverride) end end diff --git a/test/test_helper.exs b/test/test_helper.exs index d257ae06..80964ba2 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,5 +1,7 @@ Logger.configure(level: :warning) +ZiglerTest.Compiler.init() + # ZiglerTest.MakeGuides.go() ExUnit.start() diff --git a/test/unit/typespec/params_test.exs b/test/unit/typespec/params_test.exs deleted file mode 100644 index 3598f4c5..00000000 --- a/test/unit/typespec/params_test.exs +++ /dev/null @@ -1,297 +0,0 @@ -defmodule ZiglerTest.Unit.Typespec.ParamsTest do - use ExUnit.Case, async: true - - @moduletag :typespec - - alias Zig.Nif - alias Zig.Parameter - alias Zig.Return - alias Zig.Type - alias Zig.Type.Array - alias Zig.Type.Bool - alias Zig.Type.Cpointer - alias Zig.Type.Function - alias Zig.Type.Slice - alias Zig.Type.Struct - - import Type, only: :macros - - @tag :skip - test "restore" - - # defp make_spec(type) do - # %Nif{ - # name: :params_test, - # export: true, - # concurrency: Zig.Nif.Synchronous, - # file: "foo.bar", - # params: %{0 => %Parameter{type: type, cleanup: false}}, - # return: %Return{type: :void, cleanup: false} - # } - # |> Nif.render_elixir_spec() - # |> scrub - # end - # - # defp scrub(spec) do - # Macro.postwalk(spec, fn - # {a, _, nil} -> {a, [], Elixir} - # {a, _, c} -> {a, [], c} - # other -> other - # end) - # end - # - # defmacro spec(spec) do - # Macro.escape( - # quote context: Elixir do - # unquote(scrub(spec)) - # end - # ) - # end - # - # describe "when asking for a typespec return for basic types" do - # ########################################################################### - # ## INTS - # - # test "a u0-passed function is just zero" do - # assert make_spec(~t(u0)) == spec(params_test(0) :: :ok) - # end - # - # test "a u8-passed function gives appropriate bounds" do - # assert make_spec(~t(u8)) == spec(params_test(0..255) :: :ok) - # end - # - # test "a u16-passed function gives appropriate bounds" do - # assert make_spec(~t(u16)) == spec(params_test(0..0xFFFF) :: :ok) - # end - # - # test "a u32-passed function gives appropriate bounds" do - # assert make_spec(~t(u32)) == spec(params_test(0..0xFFFF_FFFF) :: :ok) - # end - # - # test "a u64-passed function gives non_neg_integer" do - # assert make_spec(~t(u64)) == spec(params_test(0..0xFFFF_FFFF_FFFF_FFFF) :: :ok) - # end - # - # test "an i32-passed function gives appropriate bounds" do - # assert make_spec(~t(i32)) == spec(params_test(-0x8000_0000..0x7FFF_FFFF) :: :ok) - # end - # - # test "an i64-passed function gives integer" do - # assert make_spec(~t(i64)) == - # spec(params_test(-0x8000_0000_0000_0000..0x7FFF_FFFF_FFFF_FFFF) :: :ok) - # end - # - # # we're not going to test c_int, c_uint, c_long, usize, etc. because these are not - # # testable across platforms in an easy way, and zig will do the platform-dependent - # # translations at compile time - # - # ########################################################################### - # ## FLOATS - # - # test "an f16-passed function gives float" do - # assert make_spec(~t(f16)) == spec(params_test(float()) :: :ok) - # end - # - # test "an f32-passed function gives float" do - # assert make_spec(~t(f32)) == spec(params_test(float()) :: :ok) - # end - # - # test "an f64-passed function gives float" do - # assert make_spec(~t(f64)) == spec(params_test(float()) :: :ok) - # end - # - # ########################################################################### - # ## BOOL - # - # test "a bool passed function is boolean" do - # assert make_spec(%Bool{}) == spec(params_test(boolean()) :: :ok) - # end - # - # ########################################################################### - # ## BEAM - # - # test "a beam.term passed function is term" do - # assert make_spec(:term) == spec(params_test(term()) :: :ok) - # end - # - # test "a e.ErlNifTerm passed function is term" do - # assert make_spec(:erl_nif_term) == spec(params_test(term()) :: :ok) - # end - # - # test "a beam.pid passed function is pid" do - # assert make_spec(:pid) == spec(params_test(pid()) :: :ok) - # end - # - # test "an enum passed function is just the optional atoms" do - # param = %Zig.Type.Enum{tags: %{ok: "ok", error: "error", maybe: "maybe"}} - # - # assert make_spec(param) == spec(params_test(:error | :maybe | :ok) :: :ok) - # end - # end - # - # describe "when asking for function parameters for arraylike collections" do - # test "a u8-slice can be passed as binary or list" do - # assert make_spec(~t([]u8)) == spec(params_test([0..255] | binary()) :: :ok) - # end - # - # test "a u8-slice with sentinel can be passed as binary or list" do - # assert make_spec(~t([:0]u8)) == spec(params_test([0..255] | binary()) :: :ok) - # end - # - # test "a int-slice can be passed as list or binary" do - # assert make_spec(~t([]i64)) == - # spec( - # params_test([-0x8000_0000_0000_0000..0x7FFF_FFFF_FFFF_FFFF] | <<_::_*64>>) :: :ok - # ) - # end - # - # test "int-slice can be passed as list or binary with next biggest byte size" do - # assert make_spec(~t([]i63)) == - # spec( - # params_test([-0x4000_0000_0000_0000..0x3FFF_FFFF_FFFF_FFFF] | <<_::_*64>>) :: :ok - # ) - # end - # - # test "a float-slice passed function can be passed list or binary" do - # assert make_spec(~t([]f32)) == spec(params_test([float()] | <<_::_*32>>) :: :ok) - # end - # - # test "manypointer without sentinel u8 can be passed as list or binary" do - # assert make_spec(~t([*]u8)) == spec(params_test([0..255] | binary()) :: :ok) - # end - # - # test "manypointer with sentinel u8 can be passed list or binary" do - # assert make_spec(~t([*:0]u8)) == spec(params_test([0..255] | binary()) :: :ok) - # end - # - # test "manypointer without sentinel u31 can be passed as list or binary" do - # assert make_spec(~t([*]u31)) == spec(params_test([0..0x7FFF_FFFF] | <<_::_*32>>) :: :ok) - # end - # - # test "manypointer without sentinel f64 can be passed list or binary" do - # assert make_spec(~t([*]f64)) == spec(params_test([float()] | <<_::_*64>>) :: :ok) - # end - # - # test "array with u8 without sentinel can be passed to binary or list" do - # assert make_spec(~t([10]u8)) == spec(params_test([0..255] | <<_::80>>) :: :ok) - # end - # - # test "array with u8 with sentinel can be passed to binary or list" do - # assert make_spec(~t([10:0]u8)) == spec(params_test([0..255] | binary()) :: :ok) - # end - # - # test "array with int can be passed binary or list of integer" do - # assert make_spec(~t([10]u63)) == - # spec(params_test([0..0x7FFF_FFFF_FFFF_FFFF] | <<_::640>>) :: :ok) - # end - # - # test "array with float can be passed binary or list of float" do - # assert make_spec(~t([10]f32)) == spec(params_test([float()] | <<_::320>>) :: :ok) - # end - # - # test "c pointer with u8 is assumed to be a string" do - # assert make_spec(~t([*c]u8)) == spec(params_test([0..255] | binary() | nil) :: :ok) - # end - # - # test "c pointer pointer of u8 is assumed to be a null terminated list of strings" do - # assert make_spec(~t([*c][*c]u8)) == - # spec(params_test([[0..255] | binary() | nil] | nil) :: :ok) - # end - # - # test "c pointer to a struct is assumed to be single struct" do - # struct = %Struct{ - # name: "Foo", - # required: %{bar: ~t(u8)}, - # optional: %{}, - # extern: true - # } - # - # assert make_spec(%Cpointer{child: struct}) == - # spec( - # params_test( - # (%{bar: 0..255} | [bar: 0..255]) - # | [%{bar: 0..255} | [bar: 0..255]] - # | nil - # ) :: - # :ok - # ) - # end - # end - # - # describe "when asking for function parameter for structs" do - # test "it can be either a map or a keyword" do - # param = %Struct{ - # name: "Foo", - # required: %{foo: ~t(f64)}, - # optional: %{bar: ~t([]u8)} - # } - # - # assert make_spec(param) == - # spec( - # params_test( - # %{optional(:bar) => [0..255] | binary(), foo: float()} - # | [bar: [0..255] | binary(), foo: float()] - # ) :: - # :ok - # ) - # end - # - # @packed %Struct{ - # name: "Foo", - # required: %{foo: ~t(f64)}, - # optional: %{bar: ~t(u64)}, - # packed: 16 - # } - # - # test "it can also be a binary if it's packed" do - # assert make_spec(@packed) == - # spec( - # params_test( - # %{optional(:bar) => 0..18_446_744_073_709_551_615, foo: float()} - # | [bar: 0..18_446_744_073_709_551_615, foo: float()] - # | <<_::128>> - # ) :: :ok - # ) - # end - # - # test "slice of packeds is what you expect" do - # assert make_spec(%Slice{child: @packed}) == - # spec( - # params_test( - # [ - # %{optional(:bar) => 0..18_446_744_073_709_551_615, foo: float()} - # | [bar: 0..18_446_744_073_709_551_615, foo: float()] - # | <<_::128>> - # ] - # | <<_::_*128>> - # ) :: :ok - # ) - # end - # - # test "array of packeds is what you expect" do - # assert make_spec(%Array{child: @packed, len: 2}) == - # spec( - # params_test( - # [ - # %{optional(:bar) => 0..18_446_744_073_709_551_615, foo: float()} - # | [bar: 0..18_446_744_073_709_551_615, foo: float()] - # | <<_::128>> - # ] - # | <<_::256>> - # ) :: :ok - # ) - # end - # end - # - # describe "when asking for optional parameters" do - # test "it adds nil to the possible parameter" do - # assert make_spec(~t(?u8)) == spec(params_test(0..255 | nil) :: :ok) - # end - # end - # - # describe "when asking for resource returns" do - # test "it marks it as a reference" do - # assert make_spec(%Zig.Type.Resource{}) == spec(params_test(reference()) :: :ok) - # end - # end -end diff --git a/test/unit/typespec/raw_test.exs b/test/unit/typespec/raw_test.exs deleted file mode 100644 index 644ba9f9..00000000 --- a/test/unit/typespec/raw_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -defmodule ZiglerTest.Unit.Typespec.RawTest do - use ExUnit.Case, async: true - - @moduletag :typespec - - alias Zig.Type - alias Zig.Type.Function - alias Zig.Type.Manypointer - alias ZiglerTest.Spec - - import Type, only: :macros - - describe "when asking for a typespec for a raw function" do - test "it returns ambiguous terms" do - result = - quote context: Elixir do - raw_test(term(), term()) :: term() - end - - assert Spec.for( - %Function{ - name: :raw_test, - arity: 2, - params: [ - :env, - ~t(i32), - %Manypointer{child: :term} - ], - return: :term - }, - raw: :beam - ) == result - end - end -end diff --git a/test/unit/typespec/return_test.exs b/test/unit/typespec/return_test.exs deleted file mode 100644 index 7d523af1..00000000 --- a/test/unit/typespec/return_test.exs +++ /dev/null @@ -1,311 +0,0 @@ -defmodule ZiglerTest.Unit.Typespec.ReturnTest do - use ExUnit.Case, async: true - - @moduletag :typespec - - alias Zig.Nif - alias Zig.Return - alias Zig.Type - alias Zig.Type.Array - alias Zig.Type.Bool - alias Zig.Type.Function - alias Zig.Type.Slice - alias Zig.Type.Struct - - import Type, only: :macros - - @tag :skip - test "restore this" - - # defp make_spec(type, as \\ :default) do - # %Nif{ - # name: :return_test, - # export: true, - # concurrency: Zig.Nif.Synchronous, - # file: "foo.bar", - # params: %{}, - # return: %Return{type: type, cleanup: false, as: as} - # } - # |> Nif.render_elixir_spec() - # |> scrub - # end - # - # defp scrub(spec) do - # Macro.postwalk(spec, fn - # {a, _, nil} -> {a, [], Elixir} - # {a, _, c} -> {a, [], c} - # other -> other - # end) - # end - # - # defmacro spec(spec) do - # Macro.escape( - # quote context: Elixir do - # unquote(scrub(spec)) - # end - # ) - # end - # - # describe "when asking for a typespec return for basic types" do - # test "a void function gives a sane result" do - # assert make_spec(:void) == spec(return_test() :: :ok) - # end - # - # ########################################################################### - # ## INTS - # - # test "a u0-returning function gives appropriate bounds" do - # assert make_spec(~t(u0)) == spec(return_test() :: 0) - # end - # - # test "a u8-returning function gives appropriate bounds" do - # assert make_spec(~t(u8)) == spec(return_test() :: 0..255) - # end - # - # test "a u16-returning function gives appropriate bounds" do - # assert make_spec(~t(u16)) == spec(return_test() :: 0..0xFFFF) - # end - # - # test "a u32-returning function gives appropriate bounds" do - # assert make_spec(~t(u32)) == spec(return_test() :: 0..0xFFFF_FFFF) - # end - # - # test "a u64-returning function gives bounds" do - # assert make_spec(~t(u64)) == spec(return_test() :: 0..0xFFFF_FFFF_FFFF_FFFF) - # end - # - # test "an i32-returning function gives appropriate bounds" do - # assert make_spec(~t(i32)) == spec(return_test() :: -0x8000_0000..0x7FFF_FFFF) - # end - # - # test "an i64-returning function gives integer" do - # assert make_spec(~t(i63)) == - # spec(return_test() :: -0x4000_0000_0000_0000..0x3FFF_FFFF_FFFF_FFFF) - # end - # - # # we're not going to test c_int, c_uint, c_long, usize, etc. because these are not - # # testable across platforms in an easy way, and zig will do the platform-dependent - # # translations at compile time - # - # ########################################################################### - # ## FLOATS - # - # test "an f16-returning function gives float" do - # assert make_spec(~t(f16)) == spec(return_test() :: float()) - # end - # - # test "an f32-returning function gives float" do - # assert make_spec(~t(f32)) == spec(return_test() :: float()) - # end - # - # test "an f64-returning function gives float" do - # assert make_spec(~t(f64)) == spec(return_test() :: float()) - # end - # - # ########################################################################### - # ## BOOL - # - # test "a bool returning function is boolean" do - # assert make_spec(%Bool{}) == spec(return_test() :: boolean()) - # end - # - # ########################################################################### - # ## BEAM - # - # test "a beam.term returning function is term" do - # assert make_spec(:term) == spec(return_test() :: term()) - # end - # - # test "a e.ErlNifTerm returning function is term" do - # assert make_spec(:erl_nif_term) == spec(return_test() :: term()) - # end - # - # test "a beam.pid returning function is pid" do - # assert make_spec(:pid) == spec(return_test() :: pid()) - # end - # - # test "an enum returning function is just the optional atoms" do - # return = %Zig.Type.Enum{tags: %{ok: "ok", error: "error", maybe: "maybe"}} - # assert make_spec(return) == spec(return_test() :: :error | :maybe | :ok) - # end - # end - # - # describe "when asking for function returns for arraylike collections" do - # test "a u8-slice returning function is special and defaults to binary" do - # assert make_spec(~t([]u8)) == spec(return_test() :: binary()) - # end - # - # test "u8-slice can be forced to return list" do - # assert make_spec(~t([]u8), :list) == spec(return_test() :: [0..255]) - # end - # - # test "a int-slice returning function is list of integer" do - # assert make_spec(~t([]i64)) == - # spec(return_test() :: [-0x8000_0000_0000_0000..0x7FFF_FFFF_FFFF_FFFF]) - # end - # - # test "int-slice can be forced to return binary" do - # assert make_spec(~t([]i64), :binary) == spec(return_test() :: <<_::_*64>>) - # end - # - # test "int-slice will pack as the biggest power of two size" do - # assert make_spec(~t([]i63), :binary) == spec(return_test() :: <<_::_*64>>) - # end - # - # test "a float-slice returning function is list of float" do - # assert make_spec(~t([]f64)) == spec(return_test() :: [float()]) - # end - # - # test "float-slice can be forced to return binary" do - # assert make_spec(~t([]f32), :binary) == spec(return_test() :: <<_::_*32>>) - # end - # - # test "manypointer with sentinel u8 defaults to binary" do - # assert make_spec(~t([*:0]u8)) == spec(return_test() :: binary()) - # end - # - # test "manypointer with sentinel u8 can be list" do - # assert make_spec(~t([*:0]u8), :list) == spec(return_test() :: [0..255]) - # end - # - # test "array with u8 defaults to binary" do - # assert make_spec(~t([10]u8)) == spec(return_test() :: <<_::80>>) - # end - # - # test "array with u8 can be forced to return list" do - # assert make_spec(~t([10]u8), :list) == spec(return_test() :: [0..255]) - # end - # - # test "array with int defaults to list of integer" do - # assert make_spec(~t([10]u64)) == spec(return_test() :: [0..0xFFFF_FFFF_FFFF_FFFF]) - # end - # - # test "array with int can be forced to return binary" do - # assert make_spec(~t([10]u64), :binary) == spec(return_test() :: <<_::640>>) - # end - # - # test "array with int, unusual size can be forced to return binary" do - # assert make_spec(~t([10]u63), :binary) == spec(return_test() :: <<_::640>>) - # end - # - # test "array with float defaults to list of float" do - # assert make_spec(~t([10]f64)) == spec(return_test() :: [float()]) - # end - # - # test "array with float can be forced to return binary" do - # assert make_spec(~t([10]f32), :binary) == spec(return_test() :: <<_::320>>) - # end - # - # test "c pointer with u8 is assumed to be a string" do - # assert make_spec(~t([*c]u8)) == spec(return_test() :: binary()) - # end - # - # test "c pointer with u8 is assumed to be a string and can be turned into a list" do - # assert make_spec(~t([*c]u8), :list) == spec(return_test() :: [0..255]) - # end - # - # test "c pointer pointer of u8 is assumed to be a null terminated list of strings" do - # assert make_spec(~t([*c][*c]u8)) == spec(return_test() :: [binary()]) - # end - # - # test "c pointer to a struct is assumed to be single struct" do - # cpointer_struct = %Struct{ - # name: "Foo", - # required: %{bar: ~t(u8)}, - # optional: %{}, - # extern: true - # } - # - # assert make_spec(cpointer_struct) == spec(return_test() :: %{bar: 0..255}) - # end - # end - # - # describe "when asking for function returns for structs" do - # test "it returns a straight map" do - # return = %Struct{ - # name: "Foo", - # required: %{foo: ~t(f64)}, - # optional: %{bar: ~t([]u8)} - # } - # - # assert make_spec(return) == spec(return_test() :: %{bar: binary(), foo: float()}) - # end - # - # @packed %Struct{ - # name: "Foo", - # required: %{foo: ~t(f64)}, - # optional: %{bar: ~t(u64)}, - # packed: 16 - # } - # - # test "it returns binary if it's packed" do - # assert make_spec(@packed, :binary) == spec(return_test() :: <<_::128>>) - # end - # - # test "slice of packeds is what you expect" do - # return = %Slice{child: @packed} - # assert make_spec(return, :binary) == spec(return_test() :: <<_::_*128>>) - # end - # - # test "array of packeds is what you expect" do - # return = %Array{child: @packed, len: 2, has_sentinel?: false} - # - # assert make_spec(return, :binary) == spec(return_test() :: <<_::256>>) - # end - # end - # - # describe "when asking for optional returns" do - # test "it adds nil to the possible return" do - # assert make_spec(~t(?u8)) == spec(return_test() :: 0..255 | nil) - # end - # end - # - # describe "when asking for resource returns" do - # test "it marks it as a reference" do - # return = %Zig.Type.Resource{} - # - # assert make_spec(return) == spec(return_test() :: reference()) - # end - # - # test "it can know if the resource will emerge as a binary" do - # return = %Zig.Type.Resource{} - # - # assert make_spec(return, :binary) == spec(return_test() :: binary()) - # end - # end - # - # describe "when there's an in-out parameter for easy_c" do - # @tag :skip - # # do - # test "it works when the length is specified" - # # assert Spec.for( - # # %Function{ - # # name: :return_test, - # # arity: 0, - # # params: [~t([*c]u8)], - # # return: :void - # # }, - # # return: [:default, length: 2, arg: 0] - # # ) == spec(return_test([0..255] | binary() | nil) :: <<_::16>>) - # # end - # - # @tag :skip - # # do - # test "it works when the length is not specified" - # # result = - # # quote context: Elixir do - # # return_test([0..255] | binary() | nil) :: binary() - # # end - # # - # # assert Spec.for( - # # %Function{ - # # name: :return_test, - # # arity: 0, - # # params: [~t([*c]u8)], - # # return: :void - # # }, - # # return: [:default, arg: 0] - # # ) == result - # # end - # end -end