ExUnit.start() defmodule TestHelpers do use ExUnit.Case def load_schema({:doge, type}) do File.read!("test/complex_schemas/" <> Atom.to_string(type) <> ".fbs") end def load_schema(type) do File.read!("test/schemas/" <> Atom.to_string(type) <> ".fbs") end def assert_full_circle(schema_type, map) do assert_full_circle(schema_type, schema_type, map) end def assert_full_circle(schema_type_ex, schema_type_fc, map) do schema_ex = Eflatbuffers.Schema.parse!(load_schema(schema_type_ex)) fb_flatc = reference_fb(schema_type_fc, map) #IO.inspect {:fb_flatc, fb_flatc}, limit: 1000 fb_ex = Eflatbuffers.write!(map, schema_ex) #IO.inspect {:fb_ex, fb_ex}, limit: 1000 #IO.inspect {:schema_ex, schema_ex}, limit: 1000 #IO.inspect {:schema_type_fc, schema_type_fc}, limit: 1000 #IO.inspect {:fb_ex_ex, Eflatbuffers.read!(fb_ex, schema_ex)}, limit: 1000 map_ex_flatc = reference_map(schema_type_fc, fb_ex) map_flatc_ex = Eflatbuffers.read!(fb_flatc, schema_ex) diff = compare_with_defaults(round_floats(map_ex_flatc), round_floats(map_flatc_ex), schema_ex) assert [] == diff end def assert_eq(schema, map, binary) do map_looped = reference_map(schema, binary) assert [] == compare_with_defaults(round_floats(map), round_floats(map_looped), Eflatbuffers.Schema.parse!(load_schema(schema))) end def reference_fb(schema, data) when is_map(data) do json = Poison.encode!(data) port = FlatbufferPort.open_port() :true = FlatbufferPort.load_schema(port, load_schema(schema)) :ok = port_response(port) :true = FlatbufferPort.json_to_fb(port, json) {:ok, reply} = port_response(port) reply end def reference_json(schema, data) when is_binary(data) do port = FlatbufferPort.open_port() :true = FlatbufferPort.load_schema(port, load_schema(schema)) :ok = port_response(port) :true = FlatbufferPort.fb_to_json(port, data) port_response(port) end def reference_map(schema, data) do {:ok, json} = reference_json(schema, data) Poison.decode!(json, [:return_maps, keys: :atoms]) end def port_response(port) do receive do {^port, {:data, data}} -> FlatbufferPort.parse_reponse(data) after 3000 -> :timeout end end def flush_port_commands do receive do {_port, {:data, _}} -> flush_port_commands() after 0 -> :ok end end def round_floats(map) when is_map(map) do map |> Enum.map(fn({k,v}) -> {k, round_floats(v)} end) |> Enum.into(%{}) end def round_floats(list) when is_list(list), do: Enum.map(list, &round_floats/1) def round_floats(float) when is_float(float), do: round(float) def round_floats(other), do: other def compare_with_defaults(a, b, schema) do {tables, _} = schema default_enums = case Map.values(tables) |> Enum.filter(fn({type, _}) -> type == :enum end) |> Enum.map(fn({:enum, options}) -> options.members end) do [] -> [] members -> Enum.map( members, fn(e) -> Atom.to_string(Map.get(e, 0)) end) end default_scalars = Map.values(tables) |> Enum.filter(fn({type, _}) -> type == :table end) |> Enum.map(fn({:table, options}) -> Enum.map(options.fields, fn({_, {_, %{ default: default }}}) -> default; (_) -> nil end) end) defaults = Enum.uniq(List.flatten(default_enums ++ default_scalars ++ [0.0, 0, false])) diff = compare(a, b) # since we write defaults to the json and flatc doesn't # we have to account for that Enum.reduce( diff, [], fn({path, {eflat, cflat}}, acc) -> case {eflat, cflat} do {:undefined, value} -> case Enum.member?(defaults, value) do true -> acc false -> [{path, {eflat, cflat}} | acc] end _ -> [{path, {eflat, cflat}} | acc] end end ) end def compare(a, b) do List.flatten(compare(a, b, [])) end def compare(same, same, _) do [] end def compare(a, b, path) when is_map(a) and is_map(b) do keys = Enum.uniq(Map.keys(a) ++ Map.keys(b)) Enum.map( keys, fn(key) -> compare(Map.get(a, key, :undefined), Map.get(b, key, :undefined), path ++ [{key}]) end ) |> List.flatten end def compare(a, b, path) when is_list(a) and is_list(b) do max_index = Enum.max([length(a), length(b)]) - 1 Enum.map( 0..max_index, fn(index) -> compare(Enum.at(a, index, :undefined), Enum.at(b, index, :undefined), path ++ [[index]]) end ) |> List.flatten end def compare(a, b, path) do {path, {a, b}} end end