Merge pull request #75 from aeternity/fix_aci
Restructure and improve ACI
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
-module(aeso_abi_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-compile(export_all).
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-define(SANDBOX(Code), sandbox(fun() -> Code end)).
|
||||
-define(DUMMY_HASH_WORD, 16#123).
|
||||
|
||||
+92
-46
@@ -2,31 +2,30 @@
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
|
||||
do_test() ->
|
||||
test_contract(1),
|
||||
test_contract(2),
|
||||
test_contract(3).
|
||||
simple_aci_test_() ->
|
||||
[{"Test contract " ++ integer_to_list(N),
|
||||
fun() -> test_contract(N) end}
|
||||
|| N <- [1, 2, 3]].
|
||||
|
||||
test_contract(N) ->
|
||||
{Contract,MapACI,DecACI} = test_cases(N),
|
||||
{ok,JSON} = aeso_aci:encode(Contract),
|
||||
?assertEqual(MapACI, jsx:decode(JSON, [return_maps])),
|
||||
?assertEqual(DecACI, aeso_aci:decode(JSON)).
|
||||
{ok,JSON} = aeso_aci:contract_interface(json, Contract),
|
||||
?assertEqual([MapACI], JSON),
|
||||
?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)).
|
||||
|
||||
test_cases(1) ->
|
||||
Contract = <<"contract C =\n"
|
||||
" function a(i : int) = i+1\n">>,
|
||||
MapACI = #{<<"contract">> =>
|
||||
#{<<"name">> => <<"C">>,
|
||||
<<"type_defs">> => [],
|
||||
<<"functions">> =>
|
||||
[#{<<"name">> => <<"a">>,
|
||||
<<"arguments">> =>
|
||||
[#{<<"name">> => <<"i">>,
|
||||
<<"type">> => [<<"int">>]}],
|
||||
<<"returns">> => <<"int">>,
|
||||
<<"stateful">> => false}]}},
|
||||
MapACI = #{contract =>
|
||||
#{name => <<"C">>,
|
||||
type_defs => [],
|
||||
functions =>
|
||||
[#{name => <<"a">>,
|
||||
arguments =>
|
||||
[#{name => <<"i">>,
|
||||
type => <<"int">>}],
|
||||
returns => <<"int">>,
|
||||
stateful => false}]}},
|
||||
DecACI = <<"contract C =\n"
|
||||
" function a : (int) => int\n">>,
|
||||
{Contract,MapACI,DecACI};
|
||||
@@ -35,42 +34,89 @@ test_cases(2) ->
|
||||
Contract = <<"contract C =\n"
|
||||
" type allan = int\n"
|
||||
" function a(i : allan) = i+1\n">>,
|
||||
MapACI = #{<<"contract">> =>
|
||||
#{<<"name">> => <<"C">>,
|
||||
<<"type_defs">> =>
|
||||
[#{<<"name">> => <<"allan">>,
|
||||
<<"typedef">> => <<"int">>,
|
||||
<<"vars">> => []}],
|
||||
<<"functions">> =>
|
||||
[#{<<"arguments">> =>
|
||||
[#{<<"name">> => <<"i">>,
|
||||
<<"type">> => [<<"int">>]}],
|
||||
<<"name">> => <<"a">>,
|
||||
<<"returns">> => <<"int">>,
|
||||
<<"stateful">> => false}]}},
|
||||
MapACI = #{contract =>
|
||||
#{name => <<"C">>,
|
||||
type_defs =>
|
||||
[#{name => <<"allan">>,
|
||||
typedef => <<"int">>,
|
||||
vars => []}],
|
||||
functions =>
|
||||
[#{arguments =>
|
||||
[#{name => <<"i">>,
|
||||
type => <<"C.allan">>}],
|
||||
name => <<"a">>,
|
||||
returns => <<"int">>,
|
||||
stateful => false}]}},
|
||||
DecACI = <<"contract C =\n"
|
||||
" function a : (int) => int\n">>,
|
||||
" type allan = int\n"
|
||||
" function a : (C.allan) => int\n">>,
|
||||
{Contract,MapACI,DecACI};
|
||||
test_cases(3) ->
|
||||
Contract = <<"contract C =\n"
|
||||
" type state = ()\n"
|
||||
" datatype event = SingleEventDefined\n"
|
||||
" datatype bert('a) = Bin('a)\n"
|
||||
" function a(i : bert(string)) = 1\n">>,
|
||||
MapACI = #{<<"contract">> =>
|
||||
#{<<"functions">> =>
|
||||
[#{<<"arguments">> =>
|
||||
[#{<<"name">> => <<"i">>,
|
||||
<<"type">> =>
|
||||
[#{<<"C.bert">> => [<<"string">>]}]}],
|
||||
<<"name">> => <<"a">>,<<"returns">> => <<"int">>,
|
||||
<<"stateful">> => false}],
|
||||
<<"name">> => <<"C">>,
|
||||
<<"type_defs">> =>
|
||||
[#{<<"name">> => <<"bert">>,
|
||||
<<"typedef">> =>
|
||||
#{<<"variant">> =>
|
||||
MapACI = #{contract =>
|
||||
#{functions =>
|
||||
[#{arguments =>
|
||||
[#{name => <<"i">>,
|
||||
type =>
|
||||
#{<<"C.bert">> => [<<"string">>]}}],
|
||||
name => <<"a">>,returns => <<"int">>,
|
||||
stateful => false}],
|
||||
name => <<"C">>,
|
||||
event => #{variant => [#{<<"SingleEventDefined">> => []}]},
|
||||
state => #{tuple => []},
|
||||
type_defs =>
|
||||
[#{name => <<"bert">>,
|
||||
typedef =>
|
||||
#{variant =>
|
||||
[#{<<"Bin">> => [<<"'a">>]}]},
|
||||
<<"vars">> => [#{<<"name">> => <<"'a">>}]}]}},
|
||||
vars => [#{name => <<"'a">>}]}]}},
|
||||
DecACI = <<"contract C =\n"
|
||||
" type state = ()\n"
|
||||
" datatype event = SingleEventDefined\n"
|
||||
" datatype bert('a) = Bin('a)\n"
|
||||
" function a : (C.bert(string)) => int\n">>,
|
||||
{Contract,MapACI,DecACI}.
|
||||
|
||||
%% Rounttrip
|
||||
aci_test_() ->
|
||||
[{"Testing ACI generation for " ++ ContractName,
|
||||
fun() -> aci_test_contract(ContractName) end}
|
||||
|| ContractName <- all_contracts()].
|
||||
|
||||
all_contracts() -> aeso_compiler_tests:compilable_contracts().
|
||||
|
||||
aci_test_contract(Name) ->
|
||||
String = aeso_test_utils:read_contract(Name),
|
||||
Opts = [{include, {file_system, [aeso_test_utils:contract_path()]}}],
|
||||
{ok, JSON} = aeso_aci:contract_interface(json, String, Opts),
|
||||
|
||||
io:format("JSON:\n~p\n", [JSON]),
|
||||
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
|
||||
|
||||
io:format("STUB:\n~s\n", [ContractStub]),
|
||||
check_stub(ContractStub, [{src_file, Name}]),
|
||||
|
||||
ok.
|
||||
|
||||
check_stub(Stub, Options) ->
|
||||
case aeso_parser:string(binary_to_list(Stub), Options) of
|
||||
{ok, Ast} ->
|
||||
try
|
||||
%% io:format("AST: ~120p\n", [Ast]),
|
||||
aeso_ast_infer_types:infer(Ast, [])
|
||||
catch _:{type_errors, TE} ->
|
||||
io:format("Type error:\n~s\n", [TE]),
|
||||
error(TE);
|
||||
_:R ->
|
||||
io:format("Error: ~p\n", [R]),
|
||||
error(R)
|
||||
end;
|
||||
{error, E} ->
|
||||
io:format("Error: ~p\n", [E]),
|
||||
error({parse_error, E})
|
||||
end.
|
||||
|
||||
|
||||
@@ -221,20 +221,15 @@ failing_contracts() ->
|
||||
, {"missing_fields_in_record_expression",
|
||||
[<<"The field x is missing when constructing an element of type r('a) (at line 7, column 40)">>,
|
||||
<<"The field y is missing when constructing an element of type r(int) (at line 8, column 40)">>,
|
||||
<<"The fields y, z are missing when constructing an element of type r('1) (at line 6, column 40)">>]}
|
||||
<<"The fields y, z are missing when constructing an element of type r('a) (at line 6, column 40)">>]}
|
||||
, {"namespace_clash",
|
||||
[<<"The contract Call (at line 4, column 10) has the same name as a namespace at (builtin location)">>]}
|
||||
, {"bad_events",
|
||||
[<<"The payload type int (at line 10, column 30) should be string">>,
|
||||
<<"The payload type alias_address (at line 12, column 30) equals address but it should be string">>,
|
||||
<<"The indexed type string (at line 9, column 25) is not a word type">>,
|
||||
<<"The indexed type alias_string (at line 11, column 25) equals string which is not a word type">>]}
|
||||
[<<"The indexed type string (at line 9, column 25) is not a word type">>,
|
||||
<<"The indexed type alias_string (at line 10, column 25) equals string which is not a word type">>]}
|
||||
, {"bad_events2",
|
||||
[<<"The event constructor BadEvent1 (at line 9, column 7) has too many non-indexed values (max 1)">>,
|
||||
<<"The event constructor BadEvent2 (at line 10, column 7) has too many indexed values (max 3)">>,
|
||||
<<"The event constructor BadEvent3 (at line 11, column 7) has too many non-indexed values (max 1)">>,
|
||||
<<"The payload type address (at line 11, column 17) should be string">>,
|
||||
<<"The payload type int (at line 11, column 26) should be string">>]}
|
||||
<<"The event constructor BadEvent2 (at line 10, column 7) has too many indexed values (max 3)">>]}
|
||||
, {"type_clash",
|
||||
[<<"Cannot unify int\n"
|
||||
" and string\n"
|
||||
@@ -261,46 +256,46 @@ failing_contracts() ->
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" address">>,
|
||||
<<"Cannot unify oracle_query('1, '2)\n"
|
||||
<<"Cannot unify oracle_query('a, 'b)\n"
|
||||
" and Remote\n"
|
||||
"when checking the type of the expression at line 25, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('1, '2)\n"
|
||||
" oracle_query('a, 'b)\n"
|
||||
"against the expected type\n"
|
||||
" Remote">>,
|
||||
<<"Cannot unify oracle_query('3, '4)\n"
|
||||
<<"Cannot unify oracle_query('c, 'd)\n"
|
||||
" and bytes(32)\n"
|
||||
"when checking the type of the expression at line 23, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('3, '4)\n"
|
||||
" oracle_query('c, 'd)\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>,
|
||||
<<"Cannot unify oracle_query('5, '6)\n"
|
||||
<<"Cannot unify oracle_query('e, 'f)\n"
|
||||
" and oracle(int, bool)\n"
|
||||
"when checking the type of the expression at line 21, column 5\n"
|
||||
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
|
||||
" oracle_query('5, '6)\n"
|
||||
" oracle_query('e, 'f)\n"
|
||||
"against the expected type\n"
|
||||
" oracle(int, bool)">>,
|
||||
<<"Cannot unify oracle('7, '8)\n"
|
||||
<<"Cannot unify oracle('g, 'h)\n"
|
||||
" and Remote\n"
|
||||
"when checking the type of the expression at line 18, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('7, '8)\n"
|
||||
" oracle('g, 'h)\n"
|
||||
"against the expected type\n"
|
||||
" Remote">>,
|
||||
<<"Cannot unify oracle('9, '10)\n"
|
||||
<<"Cannot unify oracle('i, 'j)\n"
|
||||
" and bytes(32)\n"
|
||||
"when checking the type of the expression at line 16, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('9, '10)\n"
|
||||
" oracle('i, 'j)\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>,
|
||||
<<"Cannot unify oracle('11, '12)\n"
|
||||
<<"Cannot unify oracle('k, 'l)\n"
|
||||
" and oracle_query(int, bool)\n"
|
||||
"when checking the type of the expression at line 14, column 5\n"
|
||||
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
|
||||
" oracle('11, '12)\n"
|
||||
" oracle('k, 'l)\n"
|
||||
"against the expected type\n"
|
||||
" oracle_query(int, bool)">>,
|
||||
<<"Cannot unify address\n"
|
||||
|
||||
@@ -6,10 +6,8 @@ contract Events =
|
||||
datatype event =
|
||||
Event1(indexed alias_int, indexed int, string)
|
||||
| Event2(alias_string, indexed alias_address)
|
||||
| BadEvent1(indexed string, string)
|
||||
| BadEvent2(indexed int, int)
|
||||
| BadEvent3(indexed alias_string, string)
|
||||
| BadEvent4(indexed int, alias_address)
|
||||
| BadEvent1(indexed string)
|
||||
| BadEvent2(indexed alias_string)
|
||||
|
||||
function f1(x : int, y : string) =
|
||||
Chain.event(Event1(x, x+1, y))
|
||||
|
||||
@@ -8,7 +8,6 @@ contract Events =
|
||||
| Event2(alias_string, indexed alias_address)
|
||||
| BadEvent1(string, string)
|
||||
| BadEvent2(indexed int, indexed int, indexed int, indexed address)
|
||||
| BadEvent3(address, int)
|
||||
|
||||
function f1(x : int, y : string) =
|
||||
Chain.event(Event1(x, x+1, y))
|
||||
|
||||
Reference in New Issue
Block a user