diff --git a/.gitignore b/.gitignore index 3fa8234..0b20c35 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ Thumbs.db local.properties Captures/ .externalNativeBuild/ +temp diff --git a/gajumaru-core/bin/compile b/gajumaru-core/bin/compile index 8b23f5a..833bf63 100755 --- a/gajumaru-core/bin/compile +++ b/gajumaru-core/bin/compile @@ -10,6 +10,7 @@ project_dir="$(dirname $abs_dir)" # Clean rm -rf "$project_dir/build/classes/*" +rm -f "$project_dir/Testinator.class" # Build every .java file find "$project_dir/src/main/java" -name "*.java" | xargs javac \ @@ -17,4 +18,7 @@ find "$project_dir/src/main/java" -name "*.java" | xargs javac \ -target 21 \ -d "$project_dir/build/classes" +# Build the Testinator thingy +javac -cp "build/classes" -d build src/Testinator.java + echo "Bytecode in $project_dir/build/classes/" diff --git a/gajumaru-core/bin/run b/gajumaru-core/bin/run new file mode 100755 index 0000000..79f9fc7 --- /dev/null +++ b/gajumaru-core/bin/run @@ -0,0 +1,12 @@ +#! /usr/bin/env bash + +# Copyright (c) 2026 QPQ AG . All rights reserved. +# SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-QPQ-Commercial + +set -e + +abs_dir="$(cd -P $(dirname ${BASH_SOURCE}) && pwd)" +project_dir="$(dirname $abs_dir)" + +cd "$project_dir" +java -cp "build:build/classes" Testinator $@ diff --git a/gajumaru-core/src/Testinator.java b/gajumaru-core/src/Testinator.java new file mode 100644 index 0000000..5305976 --- /dev/null +++ b/gajumaru-core/src/Testinator.java @@ -0,0 +1,56 @@ +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Base64; + +import swiss.qpq.gajumaru.core.encoding.Base58; + +public class Testinator { + public static void main(String[] args) { + if (args.length != 2) { + System.out.println("Error: Provide the test suite name and the working dir path."); + System.exit(1); + } + + try { + switch (args[0]) { + case "base64" -> { + System.out.print(base64(args[1])); + } + case "base58" -> { + System.out.print(base58(args[1])); + } + } + } catch (IOException e) { + System.err.println("Error: " + e.getMessage()); + System.exit(1); + } + } + + private static String base64(String workingPath) throws IOException { + Path testPath = Path.of(workingPath, "base64.test"); + Path encPath = Path.of(workingPath, "base64.java.txt"); + Path decPath = Path.of(workingPath, "base64.java.back"); + byte[] rawBytes = Files.readAllBytes(testPath); + byte[] encBytes = Base64.getEncoder().encode(rawBytes); + Files.write(encPath, encBytes); + byte[] readEncBytes = Files.readAllBytes(encPath); + byte[] decBytes = Base64.getDecoder().decode(readEncBytes); + Files.write(decPath, decBytes); + return encPath.toString() + " " + decPath.toString(); + } + + private static String base58(String workingPath) throws IOException { + Path testPath = Path.of(workingPath, "base58.test"); + Path encPath = Path.of(workingPath, "base58.java.txt"); + Path decPath = Path.of(workingPath, "base58.java.back"); + byte[] rawBytes = Files.readAllBytes(testPath); + String encString = Base58.encode(rawBytes); + Files.write(encPath, encString.getBytes()); + byte[] readEncBytes = Files.readAllBytes(encPath); + String readEncString = new String(readEncBytes); + byte[] decBytes = Base58.decode(readEncString); + Files.write(decPath, decBytes); + return encPath.toString() + " " + decPath.toString(); + } +} diff --git a/gajumaru-core/src/main/java/swiss/qpq/gajumaru/core/encoding/Base58.java b/gajumaru-core/src/main/java/swiss/qpq/gajumaru/core/encoding/Base58.java index a5a193e..c9a9a01 100644 --- a/gajumaru-core/src/main/java/swiss/qpq/gajumaru/core/encoding/Base58.java +++ b/gajumaru-core/src/main/java/swiss/qpq/gajumaru/core/encoding/Base58.java @@ -132,8 +132,8 @@ public final class Base58 { // The remainder of this pass is the next raw base-256 byte payload decoded[--outputStart] = (byte) remainder; - // Permanently advance past this leading index if its value has been exhausted - if (input58[i] == 0) { + // Advance past this leading index if its value has been exhausted or is 0 + while (i < input58.length && input58[i] == 0) { i++; } } diff --git a/gajumaru-core/test/.gitignore b/gajumaru-core/test/.gitignore new file mode 100644 index 0000000..20177b4 --- /dev/null +++ b/gajumaru-core/test/.gitignore @@ -0,0 +1,15 @@ +.eunit +deps +*.o +*.beam +*.plt +*.swp +erl_crash.dump +ebin/*.beam +doc/*.html +doc/*.css +doc/edoc-info +doc/erlang.png +rel/example_project +.concrete/DEV_MODE +.rebar diff --git a/gajumaru-core/test/Emakefile b/gajumaru-core/test/Emakefile new file mode 100644 index 0000000..68c7b67 --- /dev/null +++ b/gajumaru-core/test/Emakefile @@ -0,0 +1 @@ +{"src/*", [debug_info, {i, "include/"}, {outdir, "ebin/"}]}. diff --git a/gajumaru-core/test/LICENSE b/gajumaru-core/test/LICENSE new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/gajumaru-core/test/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/gajumaru-core/test/README.md b/gajumaru-core/test/README.md new file mode 100644 index 0000000..8483eee --- /dev/null +++ b/gajumaru-core/test/README.md @@ -0,0 +1,15 @@ +# GM Java <-> Erlang Tests + +The core Java libraries are all written as transform functions over data. +There is little point, therefore, in obsessing over "unit tests" and "regression testing" of Java code in Java when we have cannonical code in Erlang. + +The purpose of this utility is to instead test the Java libraries agains the Erlang libraries directly. + + +## How to run + +In the current directory simply run `zx runlocal` and reports will be placed in the `reports/` directory. + +To run a specific test suite run `zx runlocal [module names]`. + +To list module names, run `zx runlocal list`. diff --git a/gajumaru-core/test/ebin/gm_libtester.app b/gajumaru-core/test/ebin/gm_libtester.app new file mode 100644 index 0000000..fc69469 --- /dev/null +++ b/gajumaru-core/test/ebin/gm_libtester.app @@ -0,0 +1,7 @@ +{application,gm_libtester, + [{description,"An inter-language test suite for Gajumaru core libs"}, + {registered,[]}, + {included_applications,[]}, + {applications,[stdlib,kernel]}, + {vsn,"0.1.0"}, + {modules,[gmt]}]}. diff --git a/gajumaru-core/test/src/gmt.erl b/gajumaru-core/test/src/gmt.erl new file mode 100644 index 0000000..2846371 --- /dev/null +++ b/gajumaru-core/test/src/gmt.erl @@ -0,0 +1,114 @@ +%%% @doc +%%% Gajumaru Java Lib Tester: gmt +%%% @end + +-module(gmt). +-vsn("0.1.0"). +-author("Craig Everett "). +-copyright("Craig Everett "). +-license("LGPL-3.0-or-later"). + +-export([start/1]). + + +mods() -> + #{"base64" => fun base64/0, + "base58" => fun base58/0}. + + +-spec start(ArgV) -> ok + when ArgV :: [string()]. + +start([]) -> + Tests = mods(), + ok = run(Tests), + zx:silent_stop(); +start(["list"]) -> + ok = io:format("Available tests:~n"), + ok = lists:foreach(fun display/1, maps:keys(mods())), + zx:silent_stop(); +start(Mods) -> + Tests = maps:with(Mods, mods()), + ok = + case maps:size(Tests) =:= length(Mods) of + true -> + run(Tests); + false -> + NotMods = lists:subtract(Mods, maps:keys(Tests)), + ok = io:format("The following arguments are not testable module names:~n"), + lists:foreach(fun display/1, NotMods) + end, + zx:silent_stop(). + +display(Name) -> + io:format(" ~ts~n", [Name]). + +run(Tests) -> + BaseDir = filename:dirname(zx:get_home()), + ok = file:set_cwd(BaseDir), + ok = clean(), + ok = build(), + Results = maps:map(fun run/2, Tests), + io:format("Results:~n ~tp~n", [Results]). + +run(Name, Test) -> + ok = io:format("~nRunning: ~ts...~n", [Name]), + Test(). + +clean() -> + Temp = temp_dir(), + lists:foreach(fun(D) -> ok = clean(D) end, [Temp]). + +clean(Dir) -> + case file:del_dir_r(Dir) of + ok -> ok; + {error, enoent} -> ok; + Error -> Error + end. + +build() -> + Out = os:cmd("bin/compile"), + io:format("Compile: ~ts", [Out]). + +temp_dir() -> + "test/temp". + +base64() -> + TestFile = filename:join(temp_dir(), "base64.test"), + ConvFile = filename:join(temp_dir(), "base64.erlang.txt"), + ok = filelib:ensure_dir(TestFile), + ok = file:write_file(TestFile, rand:bytes(rand:uniform(5000))), + {ok, Bytes} = file:read_file(TestFile), + Base64 = base64:encode(Bytes), + ok = file:write_file(ConvFile, Base64), + Run = unicode:characters_to_list(["bin/run base64 ", temp_dir()]), + [JavaEncPath, JavaDecPath] = string:split(os:cmd(Run), " "), + {ok, E_Enc_Bytes} = file:read_file(ConvFile), + {ok, J_Enc_Bytes} = file:read_file(JavaEncPath), + E_Hash = crypto:hash(sha512, E_Enc_Bytes), + J_Hash = crypto:hash(sha512, J_Enc_Bytes), + {ok, E_Dec_Bytes} = file:read_file(TestFile), + {ok, J_Dec_Bytes} = file:read_file(JavaDecPath), + E_BinHash = crypto:hash(sha512, E_Dec_Bytes), + J_BinHash = crypto:hash(sha512, J_Dec_Bytes), + E_Hash =:= J_Hash andalso E_BinHash =:= J_BinHash. + +base58() -> + TestFile = filename:join(temp_dir(), "base58.test"), + ConvFile = filename:join(temp_dir(), "base58.erlang.txt"), + ok = filelib:ensure_dir(TestFile), + ok = file:write_file(TestFile, rand:bytes(rand:uniform(5000))), + {ok, Bytes} = file:read_file(TestFile), + Base64 = base58:binary_to_base58(Bytes), + ok = file:write_file(ConvFile, Base64), + Run = unicode:characters_to_list(["bin/run base58 ", temp_dir()]), + [JavaEncPath, JavaDecPath] = string:split(os:cmd(Run), " "), + {ok, E_Enc_Bytes} = file:read_file(ConvFile), + {ok, J_Enc_Bytes} = file:read_file(JavaEncPath), + E_Hash = crypto:hash(sha512, E_Enc_Bytes), + J_Hash = crypto:hash(sha512, J_Enc_Bytes), + {ok, E_Dec_Bytes} = file:read_file(TestFile), + {ok, J_Dec_Bytes} = file:read_file(JavaDecPath), + E_BinHash = crypto:hash(sha512, E_Dec_Bytes), + J_BinHash = crypto:hash(sha512, J_Dec_Bytes), + E_Hash =:= J_Hash andalso E_BinHash =:= J_BinHash. diff --git a/gajumaru-core/test/zomp.meta b/gajumaru-core/test/zomp.meta new file mode 100644 index 0000000..31ba140 --- /dev/null +++ b/gajumaru-core/test/zomp.meta @@ -0,0 +1,26 @@ +{name,"Gajumaru Java Lib Tester"}. +{type,cli}. +{modules,[]}. +{mod,"gmt"}. +{prefix,none}. +{author,"Craig Everett"}. +{desc,"An inter-language test suite for Gajumaru core libs"}. +{package_id,{"qpq","gm_libtester",{0,1,0}}}. +{deps,[{"otpr","hakuzaru",{0,9,1}}, + {"otpr","getopt",{1,0,2}}, + {"otpr","zj",{1,1,0}}, + {"otpr","ec_utils",{1,0,0}}, + {"otpr","eblake2",{1,0,1}}, + {"otpr","base58",{0,1,1}}, + {"otpr","gmbytecode",{3,4,1}}, + {"otpr","gmserialization",{0,1,3}}, + {"otpr","sophia",{9,0,0}}]}. +{key_name,none}. +{a_email,"craigeverett@qpq.swiss"}. +{c_email,"craigeverett@qpq.swiss"}. +{copyright,"Craig Everett"}. +{file_exts,[]}. +{license,"LGPL-3.0-or-later"}. +{repo_url,"https://git.qpq.swiss/QPQ-AG/gm-java/test"}. +{tags,[]}. +{ws_url,"https://git.qpq.swiss/QPQ-AG/gm-java/test"}.