diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66d81e3..5c55588 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,6 +45,10 @@ jobs: run: | opam exec -- dune runtest + - name: Integration tests + run: | + opam exec -- dune build @integration + - name: Format run: | opam exec -- dune build @fmt diff --git a/README.md b/README.md index a2e0048..832f8f5 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,19 @@ $ opam install object And add `object` to your project's `dune-project` or `*.opam` files. +## Tests + +The tests for `object` are split between: + * Unit tests in `test/unit` that build in-memory representations of binaries + and test they can be parsed correctly. They can be run using + `dune build @runtest`. + * Integration tests in `test/integration` that use the system toolchain to + build binaries and check they can be parsed correctly. These tests rely more + on specific versions of the toolchain that match GitHub CI runners. They can + be run using `dune build @integration`. + +Both suites can be run together with `dune build @run-all-tests`. + ## Documentation * Documentation on [ocaml.org](https://ocaml.org/p/object) diff --git a/dune b/dune new file mode 100644 index 0000000..ab9402a --- /dev/null +++ b/dune @@ -0,0 +1,8 @@ +; dune build @run-all-tests: alias that runs the unit tests (@runtest) +; and the integration tests (@integration) together. + +(alias + (name run-all-tests) + (deps + (alias_rec runtest) + (alias_rec integration))) diff --git a/dune-project b/dune-project index 171f24f..fffbf5c 100644 --- a/dune-project +++ b/dune-project @@ -1,4 +1,4 @@ -(lang dune 3.9) +(lang dune 3.23) (name object) (generate_opam_files true) (cram enable) @@ -14,8 +14,8 @@ (synopsis "A unified interface for reading and writing object file formats") (description "A unified interface for reading and writing object file formats") (depends - (ocaml (>= 4.08)) - (integers (>= 0.8.0)) - (cmdliner :with-test) + (ocaml (>= 4.14)) + (integers (>= 0.8)) + (cmdliner (and :with-test (>= 2.1))) (alcotest :with-test) (ocamlformat (and :with-dev-setup (= 0.29.0))))) diff --git a/example/dune b/example/dune index 510c280..836a509 100644 --- a/example/dune +++ b/example/dune @@ -1,4 +1,3 @@ (executables - (public_names oc_objdump generic_objdump ocnm octool oclipo ocfile) (names oc_objdump generic_objdump ocnm octool oclipo ocfile) (libraries integers object unix cmdliner)) diff --git a/object.opam b/object.opam index 7eecf05..abbd9aa 100644 --- a/object.opam +++ b/object.opam @@ -10,10 +10,10 @@ tags: ["elf" "object" "mach-o" "pe" "coff"] homepage: "https://github.com/tmcgilchrist/object" bug-reports: "https://github.com/tmcgilchrist/object/issues" depends: [ - "dune" {>= "3.9"} - "ocaml" {>= "4.08"} - "integers" {>= "0.8.0"} - "cmdliner" {with-test} + "dune" {>= "3.23"} + "ocaml" {>= "4.14"} + "integers" {>= "0.8"} + "cmdliner" {with-test & >= "2.1"} "alcotest" {with-test} "ocamlformat" {with-dev-setup & = "0.29.0"} "odoc" {with-doc} @@ -33,3 +33,5 @@ build: [ ] ] dev-repo: "git+https://github.com/tmcgilchrist/object.git" +x-maintenance-intent: ["(latest)"] +available: arch != "ppc64" diff --git a/object.opam.template b/object.opam.template new file mode 100644 index 0000000..f8be4f0 --- /dev/null +++ b/object.opam.template @@ -0,0 +1 @@ +available: arch != "ppc64" diff --git a/test/dune b/test/integration/dune similarity index 74% rename from test/dune rename to test/integration/dune index 7e93ef7..438d7ba 100644 --- a/test/dune +++ b/test/integration/dune @@ -1,109 +1,94 @@ -(test - (name test_object) - (libraries object integers alcotest)) +; Integration tests: build real object files and executables with the system +; toolchain, then verify the object library can read the details back. They +; also compare the example tools against the platform's native tools. +; + +(executables + (names + test_pe_real + test_fat + test_macho_arm64 + test_macho_amd64 + test_elf_arm64) + (libraries object integers alcotest cmdliner)) + +; Fixtures produced by the local toolchain. -(test - (name test_leb128) - (libraries object integers alcotest)) +(rule + (deps hello_world.c) + (target hello_world) + (enabled_if + (or + (= %{system} "linux") + (= %{system} "macosx"))) + (action + (run cc %{deps} -o %{target}))) -(test - (name test_pe) - (libraries object integers alcotest)) +; A Windows PE/COFF object built by cross-compiling a header-free C source. -; Exercise the reader against a real toolchain-produced COFF object, when a -; cross-compiler that targets Windows PE/COFF is available. +(rule + (deps hello_pe.c) + (target hello_pe.obj) + (enabled_if + (or + (= %{bin-available:clang} true) + (= %{bin-available:x86_64-w64-mingw32-gcc} true))) + (action + (system + "clang --target=x86_64-pc-windows-msvc -c %{deps} -o %{target} || x86_64-w64-mingw32-gcc -c %{deps} -o %{target}"))) + +; Reader acceptance tests against the real binaries above. -(test - (name test_pe_real) - (build_if +(rule + (alias integration) + (enabled_if (or (= %{bin-available:clang} true) (= %{bin-available:x86_64-w64-mingw32-gcc} true))) - (libraries object integers alcotest cmdliner) - (deps hello_pe.obj) (action (run %{exe:test_pe_real.exe} --binary %{dep:hello_pe.obj}))) -(test - (name test_fat) - (build_if +(rule + (alias integration) + (enabled_if (and (= %{system} "macosx") (= %{architecture} "arm64"))) - (libraries object integers alcotest) (action (run %{exe:test_fat.exe}))) -(test - (name test_macho_arm64) +(rule + (alias integration) (deps hello_world) - (build_if + (enabled_if (and (= %{system} "macosx") (= %{architecture} "arm64"))) - (libraries object integers alcotest cmdliner) (action (run %{exe:test_macho_arm64.exe} --binary %{dep:hello_world}))) -(test - (name test_macho_amd64) +(rule + (alias integration) (deps hello_world) - (build_if + (enabled_if (and (= %{system} "macosx") (= %{architecture} "amd64"))) - (libraries object integers alcotest cmdliner) (action (run %{exe:test_macho_amd64.exe} --binary %{dep:hello_world}))) -(test - (name test_elf_arm64) - (libraries object integers alcotest cmdliner) - (build_if +(rule + (alias integration) + (deps hello_world) + (enabled_if (and (= %{system} "linux") (= %{architecture} "arm64"))) (action (run %{exe:test_elf_arm64.exe} --binary %{dep:hello_world}))) -; Provide a plain C program to inspect - -(rule - (deps hello_world.c) - (target hello_world) - (enabled_if - (or - (= %{system} "linux") - (= %{system} "macosx"))) - (action - (run cc %{deps} -o %{target}))) - -; Provide a C program with DWARF 5 debug information to inspect. - -(rule - (deps hello_world.c) - (target hello_world_dwarf_5) - (enabled_if - (or - (= %{system} "linux") - (= %{system} "macosx"))) - (action - (run cc -gdwarf-5 %{deps} -o %{target}))) - -; A Windows PE/COFF object built by cross-compiling a header-free C source. - -(rule - (deps hello_pe.c) - (target hello_pe.obj) - (enabled_if - (or - (= %{bin-available:clang} true) - (= %{bin-available:x86_64-w64-mingw32-gcc} true))) - (action - (system - "clang --target=x86_64-pc-windows-msvc -c %{deps} -o %{target} || x86_64-w64-mingw32-gcc -c %{deps} -o %{target}"))) - -; FAT binary testing - compare our tools with system tools +; FAT binary testing - compare our example tools with the system tools on a +; known multi-arch system binary. (rule (target xcrun.otool-f.expected) @@ -118,7 +103,7 @@ (rule (target xcrun.otool-f.output) - (deps %{bin:octool}) + (deps %{exe:../../example/octool.exe}) (enabled_if (and (= %{system} "macosx") @@ -126,10 +111,10 @@ (action (with-stdout-to %{target} - (run %{bin:octool} -f /usr/bin/xcrun)))) + (run %{exe:../../example/octool.exe} -f /usr/bin/xcrun)))) (rule - (alias runtest) + (alias integration) (enabled_if (and (= %{system} "macosx") @@ -150,7 +135,7 @@ (rule (target xcrun.lipo-info.output) - (deps %{bin:oclipo}) + (deps %{exe:../../example/oclipo.exe}) (enabled_if (and (= %{system} "macosx") @@ -158,10 +143,10 @@ (action (with-stdout-to %{target} - (run %{bin:oclipo} --info /usr/bin/xcrun)))) + (run %{exe:../../example/oclipo.exe} --info /usr/bin/xcrun)))) (rule - (alias runtest) + (alias integration) (enabled_if (and (= %{system} "macosx") @@ -182,7 +167,7 @@ (rule (target xcrun.lipo-archs.output) - (deps %{bin:oclipo}) + (deps %{exe:../../example/oclipo.exe}) (enabled_if (and (= %{system} "macosx") @@ -190,10 +175,10 @@ (action (with-stdout-to %{target} - (run %{bin:oclipo} --archs /usr/bin/xcrun)))) + (run %{exe:../../example/oclipo.exe} --archs /usr/bin/xcrun)))) (rule - (alias runtest) + (alias integration) (enabled_if (and (= %{system} "macosx") @@ -214,7 +199,7 @@ (rule (target xcrun.lipo-verify_arch.output) - (deps %{bin:oclipo}) + (deps %{exe:../../example/oclipo.exe}) (enabled_if (and (= %{system} "macosx") @@ -223,7 +208,7 @@ (with-stdout-to %{target} (run - %{bin:oclipo} + %{exe:../../example/oclipo.exe} --verify_arch x86_64 --verify_arch @@ -231,7 +216,7 @@ /usr/bin/xcrun)))) (rule - (alias runtest) + (alias integration) (enabled_if (and (= %{system} "macosx") @@ -263,7 +248,7 @@ (run %{bin:file} /usr/bin/xcrun)))) (rule - (alias runtest) + (alias integration) (enabled_if (and (= %{system} "macosx") @@ -284,7 +269,7 @@ (rule (target xcrun.otool-l.output) - (deps %{bin:octool}) + (deps %{exe:../../example/octool.exe}) (enabled_if (and (= %{system} "macosx") @@ -292,10 +277,10 @@ (action (with-stdout-to %{target} - (run %{bin:octool} -l /usr/bin/xcrun)))) + (run %{exe:../../example/octool.exe} -l /usr/bin/xcrun)))) (rule - (alias runtest) + (alias integration) (enabled_if (and (= %{system} "macosx") diff --git a/test/hello_pe.c b/test/integration/hello_pe.c similarity index 100% rename from test/hello_pe.c rename to test/integration/hello_pe.c diff --git a/test/hello_world.c b/test/integration/hello_world.c similarity index 100% rename from test/hello_world.c rename to test/integration/hello_world.c diff --git a/test/test_elf_arm64.ml b/test/integration/test_elf_arm64.ml similarity index 100% rename from test/test_elf_arm64.ml rename to test/integration/test_elf_arm64.ml diff --git a/test/test_fat.ml b/test/integration/test_fat.ml similarity index 100% rename from test/test_fat.ml rename to test/integration/test_fat.ml diff --git a/test/test_macho_amd64.ml b/test/integration/test_macho_amd64.ml similarity index 100% rename from test/test_macho_amd64.ml rename to test/integration/test_macho_amd64.ml diff --git a/test/test_macho_arm64.ml b/test/integration/test_macho_arm64.ml similarity index 100% rename from test/test_macho_arm64.ml rename to test/integration/test_macho_arm64.ml diff --git a/test/test_pe_real.ml b/test/integration/test_pe_real.ml similarity index 100% rename from test/test_pe_real.ml rename to test/integration/test_pe_real.ml diff --git a/test/unit/dune b/test/unit/dune new file mode 100644 index 0000000..6d541b6 --- /dev/null +++ b/test/unit/dune @@ -0,0 +1,5 @@ +; Unit tests: exercise the library against in-memory and embedded data. + +(tests + (names test_object test_leb128 test_pe) + (libraries object integers alcotest)) diff --git a/test/test_leb128.ml b/test/unit/test_leb128.ml similarity index 100% rename from test/test_leb128.ml rename to test/unit/test_leb128.ml diff --git a/test/test_object.ml b/test/unit/test_object.ml similarity index 100% rename from test/test_object.ml rename to test/unit/test_object.ml diff --git a/test/test_pe.ml b/test/unit/test_pe.ml similarity index 100% rename from test/test_pe.ml rename to test/unit/test_pe.ml