From 5e5dd851687e40d2a70bd59bb6d6e5a3dbf835be Mon Sep 17 00:00:00 2001 From: Tim McGilchrist Date: Mon, 1 Jun 2026 19:59:49 +1000 Subject: [PATCH 1/2] Update package versions and restrict ARCH installations --- dune-project | 8 ++++---- example/dune | 1 - object.opam | 10 ++++++---- object.opam.template | 1 + test/dune | 20 ++++++++++---------- 5 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 object.opam.template 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/dune index 7e93ef7..5e767dc 100644 --- a/test/dune +++ b/test/dune @@ -118,7 +118,7 @@ (rule (target xcrun.otool-f.output) - (deps %{bin:octool}) + (deps %{exe:../example/octool.exe}) (enabled_if (and (= %{system} "macosx") @@ -126,7 +126,7 @@ (action (with-stdout-to %{target} - (run %{bin:octool} -f /usr/bin/xcrun)))) + (run %{exe:../example/octool.exe} -f /usr/bin/xcrun)))) (rule (alias runtest) @@ -150,7 +150,7 @@ (rule (target xcrun.lipo-info.output) - (deps %{bin:oclipo}) + (deps %{exe:../example/oclipo.exe}) (enabled_if (and (= %{system} "macosx") @@ -158,7 +158,7 @@ (action (with-stdout-to %{target} - (run %{bin:oclipo} --info /usr/bin/xcrun)))) + (run %{exe:../example/oclipo.exe} --info /usr/bin/xcrun)))) (rule (alias runtest) @@ -182,7 +182,7 @@ (rule (target xcrun.lipo-archs.output) - (deps %{bin:oclipo}) + (deps %{exe:../example/oclipo.exe}) (enabled_if (and (= %{system} "macosx") @@ -190,7 +190,7 @@ (action (with-stdout-to %{target} - (run %{bin:oclipo} --archs /usr/bin/xcrun)))) + (run %{exe:../example/oclipo.exe} --archs /usr/bin/xcrun)))) (rule (alias runtest) @@ -214,7 +214,7 @@ (rule (target xcrun.lipo-verify_arch.output) - (deps %{bin:oclipo}) + (deps %{exe:../example/oclipo.exe}) (enabled_if (and (= %{system} "macosx") @@ -223,7 +223,7 @@ (with-stdout-to %{target} (run - %{bin:oclipo} + %{exe:../example/oclipo.exe} --verify_arch x86_64 --verify_arch @@ -284,7 +284,7 @@ (rule (target xcrun.otool-l.output) - (deps %{bin:octool}) + (deps %{exe:../example/octool.exe}) (enabled_if (and (= %{system} "macosx") @@ -292,7 +292,7 @@ (action (with-stdout-to %{target} - (run %{bin:octool} -l /usr/bin/xcrun)))) + (run %{exe:../example/octool.exe} -l /usr/bin/xcrun)))) (rule (alias runtest) From 43e4a95fd68d8c1e29f13315d7a6166d3d333acf Mon Sep 17 00:00:00 2001 From: Tim McGilchrist Date: Thu, 4 Jun 2026 08:40:06 +1000 Subject: [PATCH 2/2] Split tests into unit tests and integration tests. Unit tests exercise in-memory representations of object files and assert properties about the parsed file. Integration tests use the system toolchain to produce binaries and validate they can be read correctly. Naturally the integration tests are sensitive to versions of the system toolchain, where possible we match the GitHub CI version of these tools as outlined in build.yml. --- .github/workflows/build.yml | 4 + README.md | 13 ++ dune | 8 ++ test/{ => integration}/dune | 157 ++++++++++----------- test/{ => integration}/hello_pe.c | 0 test/{ => integration}/hello_world.c | 0 test/{ => integration}/test_elf_arm64.ml | 0 test/{ => integration}/test_fat.ml | 0 test/{ => integration}/test_macho_amd64.ml | 0 test/{ => integration}/test_macho_arm64.ml | 0 test/{ => integration}/test_pe_real.ml | 0 test/unit/dune | 5 + test/{ => unit}/test_leb128.ml | 0 test/{ => unit}/test_object.ml | 0 test/{ => unit}/test_pe.ml | 0 15 files changed, 101 insertions(+), 86 deletions(-) create mode 100644 dune rename test/{ => integration}/dune (73%) rename test/{ => integration}/hello_pe.c (100%) rename test/{ => integration}/hello_world.c (100%) rename test/{ => integration}/test_elf_arm64.ml (100%) rename test/{ => integration}/test_fat.ml (100%) rename test/{ => integration}/test_macho_amd64.ml (100%) rename test/{ => integration}/test_macho_arm64.ml (100%) rename test/{ => integration}/test_pe_real.ml (100%) create mode 100644 test/unit/dune rename test/{ => unit}/test_leb128.ml (100%) rename test/{ => unit}/test_object.ml (100%) rename test/{ => unit}/test_pe.ml (100%) 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/test/dune b/test/integration/dune similarity index 73% rename from test/dune rename to test/integration/dune index 5e767dc..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 %{exe:../example/octool.exe}) + (deps %{exe:../../example/octool.exe}) (enabled_if (and (= %{system} "macosx") @@ -126,10 +111,10 @@ (action (with-stdout-to %{target} - (run %{exe:../example/octool.exe} -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 %{exe:../example/oclipo.exe}) + (deps %{exe:../../example/oclipo.exe}) (enabled_if (and (= %{system} "macosx") @@ -158,10 +143,10 @@ (action (with-stdout-to %{target} - (run %{exe:../example/oclipo.exe} --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 %{exe:../example/oclipo.exe}) + (deps %{exe:../../example/oclipo.exe}) (enabled_if (and (= %{system} "macosx") @@ -190,10 +175,10 @@ (action (with-stdout-to %{target} - (run %{exe:../example/oclipo.exe} --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 %{exe:../example/oclipo.exe}) + (deps %{exe:../../example/oclipo.exe}) (enabled_if (and (= %{system} "macosx") @@ -223,7 +208,7 @@ (with-stdout-to %{target} (run - %{exe:../example/oclipo.exe} + %{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 %{exe:../example/octool.exe}) + (deps %{exe:../../example/octool.exe}) (enabled_if (and (= %{system} "macosx") @@ -292,10 +277,10 @@ (action (with-stdout-to %{target} - (run %{exe:../example/octool.exe} -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