diff --git a/.gitignore b/.gitignore index fd3730f..0cc70ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,54 @@ ebin *.beam -*~ \ No newline at end of file +*~ +Makefile +Makefile.am +Makefile.in +Makefile.otp +ac-init.generated +aclocal.m4 +autom4te.cache/ +build +config.log +config.status +configure +configure.ac +create-package +doc/.run_edoc +doc/Makefile +doc/Makefile.am +doc/Makefile.in +doc/edoc-info +doc/erlang.png +doc/index.html +doc/modules-frame.html +doc/overview-summary.html +doc/packages-frame.html +doc/pokemon_pb.html +doc/protobuffs.html +doc/protobuffs_compile.html +doc/protobuffs_parser.html +doc/stylesheet.css +fw +fw-pkgin/.post-install.script_ok +fw-pkgin/.post-remove.script_ok +fw-pkgin/.pre-install.script_ok +fw-pkgin/.pre-remove.script_ok +fw-pkgin/.start.script_ok +fw-pkgin/.stop.script_ok +fw-pkgin/Makefile +fw-pkgin/Makefile.am +fw-pkgin/Makefile.in +install-sh +missing +src/Makefile +src/Makefile.am +src/Makefile.in +src/erlangprotobuffs.app +src/fw-erl-app-template.app +src/fw-erl-app-template.app.in +tests/Makefile +tests/Makefile.am +tests/Makefile.in +tests/otp-test-wrapper.sh + diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..fd331c9 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Nick Gerakines +Jacob Vorreuter +erlrc integration by Cliff Moon \ No newline at end of file diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..2774f6e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,7 @@ +* Thu Dec 03 2009 Anthony Molinaro 0.0.1 +- Fixed path for unit tests +- Have .gitignore ignore all generated files +- Fixed bool type, they were being improperly left as 0 and 1 instead of using + the true and false atoms +- Nested types should now work according to spec (as much as there is one) + diff --git a/Makefile b/Makefile deleted file mode 100644 index df10ce5..0000000 --- a/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -LIBDIR=`erl -eval 'io:format("~s~n", [code:lib_dir()])' -s init stop -noshell` -VERSION=0.3.0 -PKGNAME=erlang_protobuffs - -all: app - mkdir -p ebin/ - (cd src;$(MAKE)) - -app: - sh ebin/$(PKGNAME).app.in $(VERSION) - -test: all - prove t/*.t - -test-eqc: all - (cd t;$(MAKE)) - -clean: - (cd src;$(MAKE) clean) - (cd t; $(MAKE) clean) - rm -rf erl_crash.dump *.beam *.hrl ebin/*.app - -package: clean - @mkdir $(PKGNAME)-$(VERSION)/ && cp -rf ebin Makefile README.markdown scripts src support t $(PKGNAME)-$(VERSION) - @COPYFILE_DISABLE=true tar zcf $(PKGNAME)-$(VERSION).tgz $(PKGNAME)-$(VERSION) - @rm -rf $(PKGNAME)-$(VERSION)/ - -install: - mkdir -p $(prefix)/$(LIBDIR)/$(PKGNAME)-$(VERSION)/ebin - for i in ebin/*.beam ebin/*.app; do install $$i $(prefix)/$(LIBDIR)/$(PKGNAME)-$(VERSION)/$$i ; done diff --git a/Makefile.am.local b/Makefile.am.local new file mode 100644 index 0000000..c6d5812 --- /dev/null +++ b/Makefile.am.local @@ -0,0 +1 @@ +# put whatever (auto)make commands here, they will be included from Makefile.am diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..d03661c --- /dev/null +++ b/bootstrap @@ -0,0 +1,17 @@ +#! /bin/sh + +if test -d fw/bin + then + PATH="`pwd`/fw/bin:$PATH" + export PATH + fi + +fwb=`which fw-bootstrap` + +if test -z "$fwb" + then + echo "bootstrap: fatal: fw-bootstrap not installed or not in PATH" 1>&2 + exit 1 + fi + +"$fwb" --fw_version "0.1.31" --name erlang-protobuffs --revision none --template erlang "$@" diff --git a/configure.ac.local b/configure.ac.local new file mode 100644 index 0000000..f5fb31a --- /dev/null +++ b/configure.ac.local @@ -0,0 +1,2 @@ +dnl -- include additional autoconf commands here +dnl -- do not include AC_OUTPUT, this is called for you diff --git a/doc/Makefile.am.local b/doc/Makefile.am.local new file mode 100644 index 0000000..c6d5812 --- /dev/null +++ b/doc/Makefile.am.local @@ -0,0 +1 @@ +# put whatever (auto)make commands here, they will be included from Makefile.am diff --git a/src/overview.edoc b/doc/overview.edoc similarity index 100% rename from src/overview.edoc rename to doc/overview.edoc diff --git a/ebin/erlang_protobuffs.app.in b/ebin/erlang_protobuffs.app.in deleted file mode 100644 index 7eee717..0000000 --- a/ebin/erlang_protobuffs.app.in +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -VERSION=${1} -MODULES=`ls -1 src/*.erl | awk -F[/.] '{ print "\t\t" $2 }' | sed '$q;s/$/,/g'` - -cat > ebin/erlang_protobuffs.app << EOF -{application, erlang_protobuffs, [ - {description, "Google protobuffs implementation for Erlang."}, - {vsn, "${VERSION}"}, - {modules, [ -${MODULES} - ]}, - {registered, []}, - {applications, [kernel, stdlib, sasl, crypto, log_roller, stateless_server]} -]}. -EOF diff --git a/fw-pkgin/Makefile.am.local b/fw-pkgin/Makefile.am.local new file mode 100644 index 0000000..c6d5812 --- /dev/null +++ b/fw-pkgin/Makefile.am.local @@ -0,0 +1 @@ +# put whatever (auto)make commands here, they will be included from Makefile.am diff --git a/fw-pkgin/config b/fw-pkgin/config new file mode 100644 index 0000000..1e1a3df --- /dev/null +++ b/fw-pkgin/config @@ -0,0 +1,55 @@ +# The FW_PACKAGE_MAINTAINER field is populated with the +# environment variable FW_PACKAGE_DEFAULT_MAINTAINER if non-empty + +FW_PACKAGE_NAME="erlangprotobuffs" +FW_PACKAGE_VERSION="0.0.1" +FW_PACKAGE_MAINTAINER="cliff " +FW_PACKAGE_SHORT_DESCRIPTION="Google protobuffs implementation for Erlang." +FW_PACKAGE_DESCRIPTION=`cat README.markdown` +FW_PACKAGE_ARCHITECTURE_DEPENDENT="0" + +# Dependency information. The native syntax corresponds to Debian, +# http://www.debian.org/doc/debian-policy/ch-relationships.html +# Section 7.1 "Syntax of Relationship Fields" +# +# For other packaging systems, the syntax is translated for you. + +FW_PACKAGE_DEPENDS="" +FW_PACKAGE_CONFLICTS="" +FW_PACKAGE_PROVIDES="" +FW_PACKAGE_REPLACES="" +FW_PACKAGE_SUGGESTS="" + +FW_PACKAGE_BUILD_DEPENDS="" +FW_PACKAGE_BUILD_CONFLICTS="" +# uncomment and set manually for native hipe compilation +# ERLCFLAGS="-smp +native +\"{hipe,[o2,verbose]}\"" + +# uncomment and set manually if autodetection of modules is incorrect +# FW_ERL_APP_MODULES="" + +# uncomment and set manually if autodetection of registered processes is incorrect +# FW_ERL_APP_REGISTERED="" + +# uncomment and set manually if autodetection of start module is incorrect +# FW_ERL_APP_START_MODULE="" + +# uncomment to define start args to the start module. should be an erlang +# expression which evaluates to a list. +# FW_ERL_APP_START_ARGS="[]" + +# uncomment if the module line being generated is incorrect and you want +# to override it. +# FW_ERL_APP_MOD_LINE="{ mod, { $FW_ERL_APP_START_MODULE, $FW_ERL_APP_START_ARGS } }," + +# uncomment to define the application environment variables. should be an +# erlang expression which evaluates to a list. +# FW_ERL_APP_ENVIRONMENT="[]" + +# uncomment to indicate additional OTP applications (besides kernel and stdlib) +# that this application depends upon, comma-separated +# FW_ERL_PREREQ_APPLICATIONS_EXTRA="" + +# uncomment to add arbitrary extra content to the app file, e.g., an +# included application directive. +# FW_ERL_APP_EXTRA="" diff --git a/fw-pkgin/post-install b/fw-pkgin/post-install new file mode 100755 index 0000000..2958228 --- /dev/null +++ b/fw-pkgin/post-install @@ -0,0 +1,9 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# post-install +# +# Executed after the package is installed. +#--------------------------------------------------------------------- + +exit 0 diff --git a/fw-pkgin/post-remove b/fw-pkgin/post-remove new file mode 100755 index 0000000..6b8e5fc --- /dev/null +++ b/fw-pkgin/post-remove @@ -0,0 +1,9 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# post-remove +# +# Executed after the package is removed. +#--------------------------------------------------------------------- + +exit 0 diff --git a/fw-pkgin/pre-install b/fw-pkgin/pre-install new file mode 100755 index 0000000..d8d1722 --- /dev/null +++ b/fw-pkgin/pre-install @@ -0,0 +1,9 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# pre-install +# +# Executed before the package is installed. +#--------------------------------------------------------------------- + +exit 0 diff --git a/fw-pkgin/pre-remove b/fw-pkgin/pre-remove new file mode 100755 index 0000000..5dc7ae1 --- /dev/null +++ b/fw-pkgin/pre-remove @@ -0,0 +1,9 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# pre-remove +# +# Executed before the package is removed. +#--------------------------------------------------------------------- + +exit 0 diff --git a/fw-pkgin/start b/fw-pkgin/start new file mode 100755 index 0000000..dc16300 --- /dev/null +++ b/fw-pkgin/start @@ -0,0 +1,10 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# start +# +# Executed when the package (service) is started up. +# Not supported by all package formats. +#--------------------------------------------------------------------- + +exit 0 diff --git a/fw-pkgin/stop b/fw-pkgin/stop new file mode 100755 index 0000000..665484d --- /dev/null +++ b/fw-pkgin/stop @@ -0,0 +1,10 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# start +# +# Executed when the package (service) is shut down. +# Not supported by all package formats. +#--------------------------------------------------------------------- + +exit 0 diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index c56f980..0000000 --- a/src/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../support/include.mk - -all: $(EBIN_FILES) - -debug: - $(MAKE) DEBUG=-DDEBUG - -clean: - rm -rf $(EBIN_FILES) erl_crash.dump diff --git a/src/Makefile.am.local b/src/Makefile.am.local new file mode 100644 index 0000000..7c134d4 --- /dev/null +++ b/src/Makefile.am.local @@ -0,0 +1,14 @@ +# put whatever (auto)make commands here, they will be included from Makefile.am + +dist_erlappsrc_DATA = \ + $(wildcard *.erl) + +dist_erlappinclude_DATA = \ + $(wildcard *.hrl) + +erlappebin_SCRIPTS = \ + @FW_PACKAGE_NAME@.app \ + $(patsubst %.erl, %.beam, $(dist_erlappsrc_DATA)) + +# check_DATA = \ +# .dialyzer_ok diff --git a/src/protobuffs.erl b/src/protobuffs.erl index 6f0e5d0..3529e42 100644 --- a/src/protobuffs.erl +++ b/src/protobuffs.erl @@ -149,6 +149,11 @@ typecast(Value, SignedType) when SignedType =:= int32; SignedType =:= int64 -> end; typecast(Value, SignedType) when SignedType =:= sint32; SignedType =:= sint64 -> (Value bsr 1) bxor (-(Value band 1)); +typecast(Value, Type) when Type =:= bool -> + case Value of + 1 -> true; + _ -> false + end; typecast(Value, _) -> Value. diff --git a/src/protobuffs_compile.erl b/src/protobuffs_compile.erl index b35bc38..1fdecbf 100644 --- a/src/protobuffs_compile.erl +++ b/src/protobuffs_compile.erl @@ -28,20 +28,21 @@ scan_file(ProtoFile) when is_list(ProtoFile) -> Basename = filename:basename(ProtoFile, ".proto") ++ "_pb", Parsed = protobuffs_parser:parse_file(ProtoFile), - Messages = collect_full_messages(Parsed), + UntypedMessages = collect_full_messages(Parsed), + Messages = resolve_types (UntypedMessages), output(Basename, Messages). - + output(Basename, Messages) -> ok = write_header_include_file(Basename, Messages), BeamFile = filename:dirname(code:which(?MODULE)) ++ "/pokemon_pb.beam", {ok,{_,[{abstract_code,{_,Forms}}]}} = beam_lib:chunks(BeamFile, [abstract_code]), Forms1 = filter_forms(Messages, Forms, Basename, []), - {ok, _, Bytes, _} = compile:forms(Forms1, [return]), + {ok, _, Bytes, _Warnings} = compile:forms(Forms1, [return]), file:write_file(Basename ++ ".beam", Bytes). filter_forms(Msgs, [{attribute,L,file,{_,_}}|Tail], Basename, Acc) -> filter_forms(Msgs, Tail, Basename, [{attribute,L,file,{"src/" ++ Basename ++ ".erl",L}}|Acc]); - + filter_forms(Msgs, [{attribute,L,module,pokemon_pb}|Tail], Basename, Acc) -> filter_forms(Msgs, Tail, Basename, [{attribute,L,module,list_to_atom(Basename)}|Acc]); @@ -69,16 +70,16 @@ filter_forms(Msgs, [{function,L,encode_pikachu,1,[Clause]}|Tail], Basename, Acc) filter_forms(Msgs, [{function,L,encode,2,[Clause]}|Tail], Basename, Acc) -> filter_forms(Msgs, Tail, Basename, [expand_encode_function(Msgs, L, Clause)|Acc]); - + filter_forms(Msgs, [{function,L,decode_pikachu,1,[Clause]}|Tail], Basename, Acc) -> Functions = [begin {function,L,list_to_atom("decode_" ++ string:to_lower(Name)),1,[replace_atom(Clause, pikachu, atomize(Name))]} end || {Name, _} <- Msgs], filter_forms(Msgs, Tail, Basename, Functions ++ Acc); - + filter_forms(Msgs, [{function,L,decode,2,[Clause]}|Tail], Basename, Acc) -> filter_forms(Msgs, Tail, Basename, [expand_decode_function(Msgs, L, Clause)|Acc]); - + filter_forms(Msgs, [{function,L,to_record,2,[Clause]}|Tail], Basename, Acc) -> filter_forms(Msgs, Tail, Basename, [expand_to_record_function(Msgs, L, Clause)|Acc]); @@ -89,7 +90,7 @@ filter_forms(_, [], _, Acc) -> lists:reverse(Acc). expand_encode_function(Msgs, Line, Clause) -> {function,Line,encode,2,[filter_encode_clause(Msg, Clause) || Msg <- Msgs]}. - + filter_encode_clause({MsgName, Fields}, {clause,L,_Args,Guards,_Content}) -> Cons = lists:foldl( fun({FNum,Tag,SType,SName,_,Default}, Acc) -> @@ -104,10 +105,10 @@ filter_encode_clause({MsgName, Fields}, {clause,L,_Args,Guards,_Content}) -> end, {nil,L}, Fields), ToBin = {call,L,{atom,L,iolist_to_binary},[Cons]}, {clause,L,[{atom,L,atomize(MsgName)},{var,L,'Record'}],Guards,[ToBin]}. - + expand_decode_function(Msgs, Line, Clause) -> {function,Line,decode,2,[filter_decode_clause(Msgs, Msg, Clause) || Msg <- Msgs]}. - + filter_decode_clause(Msgs, {MsgName, Fields}, {clause,L,_Args,Guards,[_,B,C]}) -> Types = lists:keysort(1, [{FNum, list_to_atom(SName), list_to_atom(SType), decode_opts(Msgs, Tag, SType)} || {FNum,Tag,SType,SName,_,_} <- Fields]), Cons = lists:foldl( @@ -117,7 +118,7 @@ filter_decode_clause(Msgs, {MsgName, Fields}, {clause,L,_Args,Guards,[_,B,C]}) - A = {match,L,{var,L,'Types'},Cons}, C1 = replace_atom(C, pikachu, atomize(MsgName)), {clause,L,[{atom,L,atomize(MsgName)},{var,L,'Bytes'}],Guards,[A,B,C1]}. - + decode_opts(Msgs, Tag, Type) -> Opts0 = if Tag == repeated -> [repeated]; true -> [] end, case lists:keymember(Type, 1, Msgs) of @@ -126,7 +127,7 @@ decode_opts(Msgs, Tag, Type) -> false -> Opts0 end. - + expand_to_record_function(Msgs, Line, Clause) -> {function,Line,to_record,2,[filter_to_record_clause(Msg, Clause) || Msg <- Msgs]}. @@ -145,42 +146,130 @@ filter_to_record_clause({MsgName, _}, {clause,L,[_Param1,Param2],Guards,[Fold]}) %% {1,required,"string","name",number,none}]}] collect_full_messages(Data) -> collect_full_messages(Data, []). collect_full_messages([{message, Name, Fields} | Tail], Acc) -> - FieldsOut = lists:foldl( - fun (Input, TmpAcc) -> - case Input of - {_, _, _, _, _, _} -> [Input | TmpAcc]; - _ -> TmpAcc - end - end, [], Fields), - SubMessages = lists:foldl( - fun ({message, C, D}, TmpAcc) -> [{message, C, D} | TmpAcc]; - (_, TmpAcc) -> TmpAcc - end, [], Fields), - collect_full_messages(Tail ++ SubMessages, [{Name, FieldsOut} | Acc]); -collect_full_messages([{package, _Line1}, {bareword, _Line2, _PackageName}, {';', _Line3} | Tail], Acc) -> + ListName = case erlang:is_list (hd(Name)) of + true -> Name; + false -> [Name] + end, + + FieldsOut = lists:foldl( + fun (Input, TmpAcc) -> + case Input of + {_, _, _, _, _, _} -> [Input | TmpAcc]; + _ -> TmpAcc + end + end, [], Fields), + + SubMessages = lists:foldl( + fun ({message, C, D}, TmpAcc) -> [{message, [C | ListName], D} | TmpAcc]; + (_, TmpAcc) -> TmpAcc + end, [], Fields), + + collect_full_messages(Tail ++ SubMessages, [{ListName, FieldsOut} | Acc]); +collect_full_messages([{package, _Line1}, + {bareword, _Line2, _PackageName}, + {';', _Line3} | Tail], Acc) -> collect_full_messages(Tail, Acc); collect_full_messages([], Acc) -> Acc. -write_header_include_file(Basename, Messages) -> +resolve_types (Data) -> resolve_types (Data, Data, []). +resolve_types ([{TypePath, Fields} | Tail], AllPaths, Acc) -> + FieldsOut = lists:foldl( + fun (Input, TmpAcc) -> + case Input of + {Index, Rules, Type, Identifier, RealType, Other} -> + case is_scalar_type (Type) of + true -> [Input | TmpAcc]; + false -> + PossiblePaths = + case string:tokens (Type,".") of + [Type] -> + all_possible_type_paths (Type, TypePath); + FullPath -> + % handle types of the form Foo.Bar which are absolute, + % so we just convert to a type path and check it. + [lists:reverse (FullPath)] + end, + RealPath = + case find_type (PossiblePaths, AllPaths) of + false -> + throw (["Unknown Type ", Type]); + ResultType -> + ResultType + end, + [{Index, Rules, type_path_to_type (RealPath), Identifier, RealType, Other} | TmpAcc] + end; + _ -> TmpAcc + end + end, [], Fields), + resolve_types (Tail, AllPaths, + [ {type_path_to_type (TypePath), lists:reverse (FieldsOut) } | Acc]); +resolve_types ([], _, Acc) -> + Acc. + +write_header_include_file(Basename, Messages) -> {ok, FileRef} = file:open(Basename ++ ".hrl", [write]), [begin OutFields = [string:to_lower(A) || {_, _, _, A, _, _} <- lists:keysort(1, Fields)], io:format(FileRef, "-record(~s, {~s}).~n", [string:to_lower(Name), string:join(OutFields, ", ")]) end || {Name, Fields} <- Messages], file:close(FileRef). - + atomize(String) -> list_to_atom(string:to_lower(String)). - + replace_atom(Find, Find, Replace) -> Replace; - + replace_atom(Tuple, Find, Replace) when is_tuple(Tuple) -> list_to_tuple([replace_atom(Term, Find, Replace) || Term <- tuple_to_list(Tuple)]); replace_atom(List, Find, Replace) when is_list(List) -> [replace_atom(Term, Find, Replace) || Term <- List]; - + replace_atom(Other, _Find, _Replace) -> Other. - \ No newline at end of file + +is_scalar_type ("double") -> true; +is_scalar_type ("float") -> true; +is_scalar_type ("int32") -> true; +is_scalar_type ("int64") -> true; +is_scalar_type ("uint32") -> true; +is_scalar_type ("uint64") -> true; +is_scalar_type ("sint32") -> true; +is_scalar_type ("sint64") -> true; +is_scalar_type ("fixed32") -> true; +is_scalar_type ("fixed64") -> true; +is_scalar_type ("sfixed32") -> true; +is_scalar_type ("sfixed64") -> true; +is_scalar_type ("bool") -> true; +is_scalar_type ("string") -> true; +is_scalar_type ("bytes") -> true; +is_scalar_type (_) -> false. + +sublists(List) when is_list(List) -> + sublists(List,[]). +sublists([],Acc) -> + [ [] | Acc ]; +sublists(List,Acc) -> + sublists (tl (List), [ List | Acc ]). + +all_possible_type_paths (Type, TypePath) -> + lists:foldl (fun (TypeSuffix, AccIn) -> + [[Type | TypeSuffix] | AccIn] + end, + [], + sublists (TypePath)). + +find_type ([], _KnownTypes) -> + false; +find_type ([Type | TailTypes], KnownTypes) -> + case lists:keysearch (Type, 1, KnownTypes) of + false -> + find_type (TailTypes, KnownTypes); + {value, {RealType, _}} -> + RealType + end. + +type_path_to_type (TypePath) -> + string:join (lists:reverse (TypePath), "_"). + diff --git a/t/Makefile b/t/Makefile deleted file mode 100644 index 1cb97af..0000000 --- a/t/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -include ../support/include.mk - -all: $(EBIN_FILES) - -clean: - rm -rf $(EBIN_FILES) erl_crash.dump simple_pb.* - -test: $(MODULES) - -./$(MODULES): - @echo "Running tests for $@" - erl -pa ../ebin -run $@ start -run init stop -noshell diff --git a/t/protobuffs_eqc.erl b/t/protobuffs_eqc.erl deleted file mode 100644 index 52aebbb..0000000 --- a/t/protobuffs_eqc.erl +++ /dev/null @@ -1,276 +0,0 @@ -%%% erlc -o ebin t/*.erl -pa ebin -%%% erl -name eqc_pb -pa ebin -%%% eqc_gen:sample(protobuffs_eqc:protobuff_data()). -%%% eqc:quickcheck(protobuffs_eqc:prop_encode_decode1()). -%%% eqc:quickcheck(protobuffs_eqc:prop_encode_decode2()). -%%% eqc:quickcheck(protobuffs_eqc:prop_encode_decode3()). -%%% -%%% File : protobuffs_eqc.erl -%%% Author : -%%% Description : QuickCheck specification used in class for -%%% protobuffs-0.2 -%%% Created : 27 Apr 2009 by --module(protobuffs_eqc). - --include_lib("eqc/include/eqc.hrl"). - --compile(export_all). - --define(Mach_Eps, 1.1920928955078125e-7). --define(NotYetImplemented(Cond,Prop), ?IMPLIES(not (Cond),Prop)). - -%% Properties - -prop_encode_decode1() -> - ?FORALL({FieldNum,Data,Type}, protobuff_data(), - collect(Type, - begin - {{N, RData}, <<>>} = protobuffs:decode(protobuffs:encode(FieldNum, Data, Type), Type), - FieldNum =:= N andalso - (compare(Data, RData) orelse foreign_type(Type, Data, RData)) - end)). - -prop_encode_decode2() -> - ?FORALL({FieldNum,Data,Type}, fault_rate(5,10,protobuff_data()), - case catch protobuffs:encode(FieldNum,Data,Type) of - {'EXIT', _} -> - not in_range(Data,Type); - Bin -> - {{N, RData}, <<>>} = protobuffs:decode(Bin, Type), - in_range(Data,Type) andalso - FieldNum =:= N andalso - (compare(Data,RData) orelse foreign_type(Type,Data,RData)) - end). - -prop_encode_decode3() -> - ?FORALL(Many, protobuff_many(), - begin - Sorted = lists:keysort(1, Many), - IOList = [protobuffs:encode(FNum,Data,Type) || {FNum,Data,Type} <- Sorted], - Bin = iolist_to_binary(IOList), - {Decoded0,_} = lists:foldl( - fun({_,_,Type}, {Acc, Bin1}) -> - {Val, Rest} = protobuffs:decode(Bin1, Type), - {[Val|Acc], Rest} - end, {[],Bin}, Sorted), - Decoded = lists:reverse(Decoded0), - lists:foldl( - fun (_, false) -> false; - (I, true) -> - {FNum1, Data1, Type1} = lists:nth(I, Sorted), - {FNum2, Data2} = lists:nth(I, Decoded), - (FNum1 =:= FNum2 andalso - (compare(Data1,Data2) orelse foreign_type(Type1,Data1,Data2))) - end, true, lists:seq(1, length(Sorted))) - end). - -prop_encode_decode4() -> - ?FORALL({ProtoName, Msgs}, protobuff_msgs(), - begin - code:delete(list_to_atom(ProtoName)), - code:purge(list_to_atom(ProtoName)), - protobuffs_compile:output(ProtoName, Msgs), - ?FORALL(Set, protobuff_set(Defs), - begin - Record = build_record_from_defs(MsgName, Defs, Set), - case catch apply(list_to_atom(ProtoName), list_to_atom("encode_" ++ MsgName), [Record]) of - {'EXIT', {error, {required_field_is_undefined, FNum, _}}} -> - {value, {_, required, _, _, _, _}} = lists:keysearch(FNum, 1, Defs), - true; - Bin when is_binary(Bin) -> - Record1 = apply(list_to_atom(ProtoName), list_to_atom("decode_" ++ MsgName), [Bin]), - lists:foldl( - fun ({A,B}, true) -> - io:format("compare ~p and ~p~n", [A,B]), - compare(A,B); - (_, false) -> - false - end, true, lists:zip(tuple_to_list(Record), tuple_to_list(Record1))) - end - end) - end). - -%% Data generators - -protobuff_msgs() -> - {string(), list({msg_name(), protobuff_defs()})}. - -protobuff_many() -> - list(protobuff_data()). - -protobuff_set(Defs) -> - [begin - {FNum, Name, field_value(Tag, Type)} - end || {FNum, Tag, Type, Name, _, _Default} <- Defs]. - -protobuff_data() -> - fault({field_num(), int(80), oneof([int32,uint32,int64,uint64,sint32,sint64])}, - oneof([ - {field_num(), int(32), int32}, - {field_num(), uint(32), uint32}, - {field_num(), int(64), int64}, - {field_num(), uint(64), uint64}, - {field_num(), bool(), bool}, - {field_num(), sint(32), sint32}, - {field_num(), sint(64), sint64}, - {field_num(), real(), float}, - {field_num(), real(), double}, - {field_num(), list(char()), string}, - {field_num(), binary(), bytes} - ]) - ). - -protobuff_defs() -> - ?SUCHTHAT(D,orderedlist(protobuff_def()),length(D) > 0). - -protobuff_def() -> - oneof([ - {field_num(), tag(), "int32", field_name(), number, oneof([none, int(32)])}, - {field_num(), tag(), "uint32", field_name(), number, oneof([none, uint(32)])}, - {field_num(), tag(), "int64", field_name(), number, oneof([none, int(64)])}, - {field_num(), tag(), "uint64", field_name(), number, oneof([none, uint(64)])}, - {field_num(), tag(), "bool", field_name(), number, oneof([none, bool()])}, - {field_num(), tag(), "sint32", field_name(), number, oneof([none, sint(32)])}, - {field_num(), tag(), "sint64", field_name(), number, oneof([none, sint(64)])}, - {field_num(), tag(), "float", field_name(), number, oneof([none, real()])}, - {field_num(), tag(), "double", field_name(), number, oneof([none, real()])}, - {field_num(), tag(), "string", field_name(), number, oneof([none, list(char())])}, - {field_num(), tag(), "bytes", field_name(), number, oneof([none, binary()])} - ]). - -field_value(repeated, "int32") -> oneof([undefined, list(int(32))]); -field_value(repeated, "uint32") -> oneof([undefined, list(uint(32))]); -field_value(repeated, "int64") -> oneof([undefined, list(int(64))]); -field_value(repeated, "uint64") -> oneof([undefined, list(uint(64))]); -field_value(repeated, "bool") -> oneof([undefined, list(bool())]); -field_value(repeated, "sint32") -> oneof([undefined, list(sint(32))]); -field_value(repeated, "sint64") -> oneof([undefined, list(sint(64))]); -field_value(repeated, "float") -> oneof([undefined, list(real())]); -field_value(repeated, "double") -> oneof([undefined, list(real())]); -field_value(repeated, "string") -> oneof([undefined, list(list(char()))]); -field_value(repeated, "bytes") -> oneof([undefined, list(binary())]); -field_value(_, "int32") -> oneof([undefined, int(32)]); -field_value(_, "uint32") -> oneof([undefined, uint(32)]); -field_value(_, "int64") -> oneof([undefined, int(64)]); -field_value(_, "uint64") -> oneof([undefined, uint(64)]); -field_value(_, "bool") -> oneof([undefined, bool()]); -field_value(_, "sint32") -> oneof([undefined, sint(32)]); -field_value(_, "sint64") -> oneof([undefined, sint(64)]); -field_value(_, "float") -> oneof([undefined, real()]); -field_value(_, "double") -> oneof([undefined, real()]); -field_value(_, "string") -> oneof([undefined, list(char())]); -field_value(_, "bytes") -> oneof([undefined, binary()]). - -field_num() -> - ?SUCHTHAT(N,nat(),N>0). - -tag() -> - oneof([optional, required, repeated]). - -field_name() -> - ?SUCHTHAT(N,string(),length(N)>0). - -msg_name() -> - ?SUCHTHAT(N,string(),length(N)>0). - -string() -> - list(oneof([choose(97,122), choose(65,90)])). - -%% Internal Functions - -foreign_type(bool,false,0) -> - true; -foreign_type(bool,true,1) -> - true; -foreign_type(_,_,_) -> - false. - -prop_varint() -> - ?FORALL(Base,oneof([32,64]), - ?FORALL(I,int(Base), - begin - {Bits,Data} = decompose(protobuffs:encode_varint(I)), - right_bits(Bits) andalso - concatenate(Data) == I - end - ) - ). - -build_record_from_defs(MsgName, Defs, Set) -> - lists:foldl( - fun({FNum, _Tag, _Type, _Name, _, _Default}, Acc) -> - {value, {_,_,Value}} = lists:keysearch(FNum, 1, Set), - erlang:append_element(Acc, Value) - end, {list_to_atom(MsgName)}, Defs). - -%% Bits are in reverse order: First bit should be zero, rest should be 1 -right_bits([0|Rest]) -> - lists:all(fun(B) -> B==1 end,Rest). - -int(Base) -> - ?LET(I,uint(Base), - begin - << N:Base/signed >> = <>, N - end - ). - -uint(Base) -> - oneof([ choose(0,exp(B)) || B<-lists:seq(1,Base)]). - -sint(Base) -> - int(Base). - -exp(1) -> - 2; -exp(N) -> - 2*exp(N-1). - -decompose(<>) -> - {[Bit],<>}; -decompose(<>) -> - {Bs,Ds} = decompose(Rest), - {Bs++[Bit],<>}. - -concatenate(Bin) -> - S = bit_size(Bin), - << N:S >> = Bin, - N. - -in_range(Int,int32) -> - fitbits(Int,32); -in_range(Int,sint32) -> - fitbits(abs(Int),31); -in_range(Int,uint32) -> - fitbits(Int,32); -in_range(Int,int64) -> - fitbits(Int,64); -in_range(Int,sint64) -> - fitbits(abs(Int),63); -in_range(Int,uint64) -> - fitbits(Int,64); -in_range(Float,float) -> - fitbits(Float,32); -in_range(Float,double) -> - fitbits(Float,64); -in_range(_,string) -> - true; -in_range(_,bytes) -> - true; -in_range(false,bool) -> - true; -in_range(true,bool) -> - true. - -compare(Float1, Float2) when is_float(Float1), is_float(Float2) -> - (abs(Float1 - Float2) =< ?Mach_Eps); -compare(String, Binary) when is_list(String), is_binary(Binary) -> - String =:= binary_to_list(Binary); -compare(A,A) -> true; -compare(_,_) -> false. - -fitbits(Float,32) when is_float(Float) -> true; -fitbits(Float,64) when is_float(Float) -> true; -fitbits(Int,Bits) -> - RestBits = 80-Bits, - << NoFit:RestBits, _:Bits >> = <>, - NoFit == 0. diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..882e541 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,5 @@ +nested1_pb.hrl +nested2_pb.hrl +nested3_pb.hrl +nested4_pb.hrl +nested5_pb.hrl diff --git a/tests/Makefile.am.local b/tests/Makefile.am.local new file mode 100644 index 0000000..fd5f7a9 --- /dev/null +++ b/tests/Makefile.am.local @@ -0,0 +1,2 @@ +TESTS = \ + runtests.sh diff --git a/t/empty.proto b/tests/empty.proto similarity index 100% rename from t/empty.proto rename to tests/empty.proto diff --git a/t/hasdefault.proto b/tests/hasdefault.proto similarity index 100% rename from t/hasdefault.proto rename to tests/hasdefault.proto diff --git a/tests/nested1.proto b/tests/nested1.proto new file mode 100644 index 0000000..1a01428 --- /dev/null +++ b/tests/nested1.proto @@ -0,0 +1,18 @@ +message Person { + required string name = 1; + required int32 id = 2; + optional string email = 3; + + message PhoneNumber { + required string number = 1; + message PhoneType { + optional int32 mobile = 1; + optional int32 home = 2; + optional int32 work = 3; + } + optional PhoneType type = 2; + } + + repeated PhoneNumber phone = 4; +} + diff --git a/tests/nested2.proto b/tests/nested2.proto new file mode 100644 index 0000000..79e2a7d --- /dev/null +++ b/tests/nested2.proto @@ -0,0 +1,19 @@ +message Outer { // Level 0 + message MiddleAA { // Level 1 + message Inner { // Level 2 + required int64 ival = 1; + optional bool booly = 2; + } + optional Inner inner = 1; + } + optional MiddleAA middleaa = 1; + message MiddleBB { // Level 1 + message Inner { // Level 2 + required int32 ival = 1; + optional bool booly = 2; + } + optional Inner inner = 1; + } + optional MiddleBB middlebb = 2; +} + diff --git a/tests/nested3.proto b/tests/nested3.proto new file mode 100644 index 0000000..4ddecb0 --- /dev/null +++ b/tests/nested3.proto @@ -0,0 +1,15 @@ +message Outer { + message Middle { + message Inner { + optional bool foo = 1; + } + optional Inner inner = 2; + optional Other other = 3; + } + + message Other { + optional bool bar = 1; + } + + optional Middle middle = 1; +} diff --git a/tests/nested4.proto b/tests/nested4.proto new file mode 100644 index 0000000..ba84a44 --- /dev/null +++ b/tests/nested4.proto @@ -0,0 +1,15 @@ +message Outer { + optional Middle middle = 1; + + message Other { + optional bool bar = 1; + } + message Middle { + optional Inner inner = 2; + optional Other other = 3; + message Inner { + optional bool foo = 1; + } + } +} + diff --git a/tests/nested5.proto b/tests/nested5.proto new file mode 100644 index 0000000..a9c34d8 --- /dev/null +++ b/tests/nested5.proto @@ -0,0 +1,9 @@ +message First { + message Inner { + optional bool foo = 1; + } + optional Inner inner = 1; +} +message Second { + required First.Inner inner = 1; +} diff --git a/t/protobuffs_t_001.t b/tests/protobuffs_t_001.t similarity index 89% rename from t/protobuffs_t_001.t rename to tests/protobuffs_t_001.t index 4b17fa6..23e79ad 100644 --- a/t/protobuffs_t_001.t +++ b/tests/protobuffs_t_001.t @@ -1,6 +1,6 @@ #!/usr/bin/env escript %% -*- erlang -*- -%%! -pa ./ebin -sasl errlog_type error -boot start_sasl -noshell +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell main(_) -> etap:plan(8), diff --git a/t/protobuffs_t_002.t b/tests/protobuffs_t_002.t similarity index 82% rename from t/protobuffs_t_002.t rename to tests/protobuffs_t_002.t index f2583f3..4f82cfc 100644 --- a/t/protobuffs_t_002.t +++ b/tests/protobuffs_t_002.t @@ -1,6 +1,6 @@ #!/usr/bin/env escript %% -*- erlang -*- -%%! -pa ./ebin -sasl errlog_type error -boot start_sasl -noshell +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell main(_) -> etap:plan(3), diff --git a/t/protobuffs_t_003.t b/tests/protobuffs_t_003.t similarity index 92% rename from t/protobuffs_t_003.t rename to tests/protobuffs_t_003.t index 7803edd..2255a40 100644 --- a/t/protobuffs_t_003.t +++ b/tests/protobuffs_t_003.t @@ -1,9 +1,9 @@ #!/usr/bin/env escript %% -*- erlang -*- -%%! -pa ./ebin -noshell +%%! -pa ../src -noshell main(_) -> - etap:plan(18), + etap:plan(20), Tests = [ {8, uint32}, {16, uint32}, @@ -22,7 +22,9 @@ main(_) -> {<<"It's a secret to everyone.">>, string}, {<<4,8,15,16,23,42>>, bytes}, {3.141592025756836, float}, - {1.00000000000000022204460492503130808472633361816406, double} + {1.00000000000000022204460492503130808472633361816406, double}, + {false, bool}, + {true, bool} ], lists:foreach( fun(Test) -> diff --git a/t/protobuffs_t_005.t b/tests/protobuffs_t_005.t similarity index 79% rename from t/protobuffs_t_005.t rename to tests/protobuffs_t_005.t index dd2af70..913f5ca 100644 --- a/t/protobuffs_t_005.t +++ b/tests/protobuffs_t_005.t @@ -1,13 +1,13 @@ #!/usr/bin/env escript %% -*- erlang -*- -%%! -pa ./ebin -sasl errlog_type error -boot start_sasl -noshell +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell -record(location, {region, country}). -record(person, {name, address, phone_number, age, location}). main(_) -> etap:plan(2), - etap:is(protobuffs_compile:scan_file("t/simple.proto"), ok, "simple.proto compiled"), + etap:is(protobuffs_compile:scan_file("simple.proto"), ok, "simple.proto compiled"), Person = #person{ name = "Nick", diff --git a/t/protobuffs_t_006.t b/tests/protobuffs_t_006.t similarity index 88% rename from t/protobuffs_t_006.t rename to tests/protobuffs_t_006.t index b062e8b..8f6f061 100644 --- a/t/protobuffs_t_006.t +++ b/tests/protobuffs_t_006.t @@ -1,13 +1,13 @@ #!/usr/bin/env escript %% -*- erlang -*- -%%! -pa ./ebin -sasl errlog_type error -boot start_sasl -noshell +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell -record(location, {region, country}). -record(person, {name, address, phone_number, age, location}). main(_) -> etap:plan(3), - etap:is(protobuffs_compile:scan_file("t/simple.proto"), ok, "simple.proto compiled"), + etap:is(protobuffs_compile:scan_file("simple.proto"), ok, "simple.proto compiled"), Fields1 = [ {1, "California", string}, diff --git a/t/protobuffs_t_007.t b/tests/protobuffs_t_007.t similarity index 92% rename from t/protobuffs_t_007.t rename to tests/protobuffs_t_007.t index 1ed6f23..6dbf7d6 100644 --- a/t/protobuffs_t_007.t +++ b/tests/protobuffs_t_007.t @@ -1,13 +1,13 @@ #!/usr/bin/env escript %% -*- erlang -*- -%%! -pa ./ebin -sasl errlog_type error -boot start_sasl -noshell +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell -record(location, {region, country}). -record(person, {name, address, phone_number, age, hobbies, locations}). main(_) -> etap:plan(5), - etap:is(protobuffs_compile:scan_file("t/repeater.proto"), ok, "repeater.proto compiled"), + etap:is(protobuffs_compile:scan_file("repeater.proto"), ok, "repeater.proto compiled"), Fields1 = [ {1, "Lyon", string}, diff --git a/t/protobuffs_t_008.t b/tests/protobuffs_t_008.t similarity index 82% rename from t/protobuffs_t_008.t rename to tests/protobuffs_t_008.t index 0cd200a..0de7430 100644 --- a/t/protobuffs_t_008.t +++ b/tests/protobuffs_t_008.t @@ -1,13 +1,13 @@ #!/usr/bin/env escript %% -*- erlang -*- -%%! -pa ./ebin -sasl errlog_type error -boot start_sasl -noshell +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell -record(location, {region, country}). -record(person, {name, address, phone_number, age, location}). main(_) -> etap:plan(2), - etap:is(protobuffs_compile:scan_file("t/hasdefault.proto"), ok, "hasdefault.proto created"), + etap:is(protobuffs_compile:scan_file("hasdefault.proto"), ok, "hasdefault.proto created"), Person = #person { name = "Nick", diff --git a/t/protobuffs_t_009.t b/tests/protobuffs_t_009.t similarity index 75% rename from t/protobuffs_t_009.t rename to tests/protobuffs_t_009.t index 3f7e613..d7242ba 100644 --- a/t/protobuffs_t_009.t +++ b/tests/protobuffs_t_009.t @@ -1,13 +1,13 @@ #!/usr/bin/env escript %% -*- erlang -*- -%%! -pa ./ebin -sasl errlog_type error -boot start_sasl -noshell +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell -record(location, {region, country}). -record(person, {name, address, phone_number, age, location}). main(_) -> etap:plan(2), - etap:is(protobuffs_compile:scan_file("t/hasdefault.proto"), ok, "hasdefault.proto created"), + etap:is(protobuffs_compile:scan_file("hasdefault.proto"), ok, "hasdefault.proto created"), case catch hasdefault_pb:encode_person(#person{}) of {'EXIT', {error, {required_field_is_undefined,1,string}}} -> diff --git a/tests/protobuffs_t_010.t b/tests/protobuffs_t_010.t new file mode 100644 index 0000000..813ca00 --- /dev/null +++ b/tests/protobuffs_t_010.t @@ -0,0 +1,30 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell + +%% TODO: really just want to include_lib, but unfortunately thats not +%% possible at the moment, so need to hand include them +%% -include_lib("nested1_pb.hrl"). +-record(person_phonenumber_phonetype, {mobile, home, work}). +-record(person_phonenumber, {number, type}). +-record(person, {name, id, email, phone}). + +main(_) -> + etap:plan(2), + etap:is(protobuffs_compile:scan_file("nested1.proto"), ok, "nested.proto created"), + + Person = #person{ + name = "Anthony", + id = 655350, + email = "anthony@example.com", + phone = [#person_phonenumber{ + number = "+1 (000) 555-1234", + type = #person_phonenumber_phonetype { mobile = 25 } + }] + }, + + Bin = nested1_pb:encode_person (Person), + + etap:is (nested1_pb:decode_person (Bin), Person, "Encoded and decoded person"), + + etap:end_tests(). diff --git a/tests/protobuffs_t_011.t b/tests/protobuffs_t_011.t new file mode 100644 index 0000000..8583fe8 --- /dev/null +++ b/tests/protobuffs_t_011.t @@ -0,0 +1,37 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell + +%% TODO: really just want to include_lib, but unfortunately thats not +%% possible at the moment, so need to hand include them +%% -include_lib("nested2_pb.hrl"). +-record(outer_middleaa_inner, {ival, booly}). +-record(outer_middlebb_inner, {ival, booly}). +-record(outer_middleaa, {inner}). +-record(outer_middlebb, {inner}). +-record(outer, {middleaa, middlebb}). + +main(_) -> + etap:plan(2), + etap:is(protobuffs_compile:scan_file("nested2.proto"), ok, "nested2.proto created"), + + Outer = #outer{ + middleaa = #outer_middleaa { + inner = #outer_middleaa_inner { + ival = 16#7aaabbbcccdddeee, + booly = false + } + }, + middlebb = #outer_middlebb { + inner = #outer_middlebb_inner { + ival = 16#7aaabbbc, + booly = true + } + } + }, + + Bin = nested2_pb:encode_outer (Outer), + etap:is (nested2_pb:decode_outer (Bin), Outer, "Encoded and decoded outer"), + + etap:end_tests(). + diff --git a/tests/protobuffs_t_012.t b/tests/protobuffs_t_012.t new file mode 100644 index 0000000..f609d9a --- /dev/null +++ b/tests/protobuffs_t_012.t @@ -0,0 +1,51 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell + +%% TODO: really just want to include_lib, but unfortunately thats not +%% possible at the moment, so need to hand include them +%% -include_lib("nested3_pb.hrl"). +%% or +%% -include_lib("nested4_pb.hrl"). +-record(outer, {middle}). +-record(outer_other, {bar}). +-record(outer_middle, {inner, other}). +-record(outer_middle_inner, {foo}). + +main(_) -> + etap:plan(4), + etap:is(protobuffs_compile:scan_file("nested3.proto"), ok, "nested3.proto created"), + + Outer = #outer{ + middle = #outer_middle { + inner = #outer_middle_inner { + foo = false + }, + other = #outer_other { + bar = true + } + } + }, + + Bin = nested3_pb:encode_outer (Outer), + etap:is (nested3_pb:decode_outer (Bin), Outer, "Encoded and decoded outer"), + + %% nested4 is just a rearranged nested3 so I can check that ordering does + %% not affect things + etap:is(protobuffs_compile:scan_file("nested4.proto"), ok, "nested4.proto created"), + + Outer2 = #outer{ + middle = #outer_middle { + inner = #outer_middle_inner { + foo = false + }, + other = #outer_other { + bar = true + } + } + }, + + Bin2 = nested4_pb:encode_outer (Outer2), + etap:is (nested4_pb:decode_outer (Bin2), Outer2, "Encoded and decoded outer"), + + etap:end_tests(). diff --git a/tests/protobuffs_t_013.t b/tests/protobuffs_t_013.t new file mode 100644 index 0000000..3fbac6e --- /dev/null +++ b/tests/protobuffs_t_013.t @@ -0,0 +1,30 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +%%! -pa ../src -sasl errlog_type error -boot start_sasl -noshell + +%% TODO: really just want to include_lib, but unfortunately thats not +%% possible at the moment, so need to hand include them +%% -include_lib("nested5_pb.hrl"). +-record(first, {inner}). +-record(second, {inner}). +-record(first_inner, {foo}). + +main(_) -> + etap:plan(3), + etap:is(protobuffs_compile:scan_file("nested5.proto"), ok, "nested5.proto created"), + + First = #first { + inner = #first_inner { foo = false } + }, + + BinFirst = nested5_pb:encode_first (First), + etap:is (nested5_pb:decode_first (BinFirst), First, "Encoded and decoded first"), + + Second = #second { + inner = #first_inner { foo = true } + }, + + BinSecond = nested5_pb:encode_second (Second), + etap:is (nested5_pb:decode_second (BinSecond), Second, "Encoded and decoded second"), + + etap:end_tests(). diff --git a/t/repeater.proto b/tests/repeater.proto similarity index 100% rename from t/repeater.proto rename to tests/repeater.proto diff --git a/tests/runtests.sh b/tests/runtests.sh new file mode 100755 index 0000000..cacb43f --- /dev/null +++ b/tests/runtests.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +prove *.t \ No newline at end of file diff --git a/t/simple.proto b/tests/simple.proto similarity index 100% rename from t/simple.proto rename to tests/simple.proto