about summary refs log tree commit diff
path: root/third_party/bazel/rules_haskell/tests
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/bazel/rules_haskell/tests')
-rw-r--r--third_party/bazel/rules_haskell/tests/BUILD.bazel322
-rw-r--r--third_party/bazel/rules_haskell/tests/RunTests.hs155
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-custom-main/BUILD.bazel17
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-custom-main/Main.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-dynamic/BUILD.bazel14
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-dynamic/Main.hs3
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-exe-path/BUILD.bazel13
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-exe-path/Main.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-indirect-cbits/BUILD.bazel36
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Main.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Wrapper.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Wrapper2.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/BUILD.bazel118
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/HsLib.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/Main.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/c-lib.c1
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-simple/BUILD.bazel15
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-simple/Main.hs3
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-compiler-flags/BUILD.bazel18
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-compiler-flags/Main.hs13
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-data/BUILD.bazel31
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-data/bin1-input1
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-data/bin1.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-data/bin2.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-import/BUILD.bazel26
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-import/Main.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-import/src/Lib.hs13
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/BUILD.bazel27
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/HsLib.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/Main.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/BUILD.bazel25
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/Main.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/src/Lib.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-lib/BUILD.bazel27
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-lib/Main.hs11
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-lib/src/Lib.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-link-flags/BUILD.bazel17
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-link-flags/Main.hs3
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-main/BUILD.bazel14
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-main/MainIsHere.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-plugin/BUILD.bazel53
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-plugin/Main.hs3
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-plugin/Plugin.hs15
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-prebuilt/BUILD.bazel19
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-prebuilt/Main.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-sysdeps/BUILD.bazel17
-rw-r--r--third_party/bazel/rules_haskell/tests/binary-with-sysdeps/Main.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/c-compiles-still/BUILD.bazel15
-rw-r--r--third_party/bazel/rules_haskell/tests/c-compiles-still/Foo.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/c-compiles/BUILD.bazel26
-rw-r--r--third_party/bazel/rules_haskell/tests/c-compiles/Lib.hs10
-rw-r--r--third_party/bazel/rules_haskell/tests/c-compiles/Main.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/c-compiles/c-compiles.c1
-rw-r--r--third_party/bazel/rules_haskell/tests/c2hs/BUILD.bazel36
-rw-r--r--third_party/bazel/rules_haskell/tests/c2hs/Bar.chs6
-rw-r--r--third_party/bazel/rules_haskell/tests/c2hs/repo/BUILD.bazel10
-rw-r--r--third_party/bazel/rules_haskell/tests/c2hs/repo/Baz.chs6
-rw-r--r--third_party/bazel/rules_haskell/tests/c2hs/repo/WORKSPACE1
-rw-r--r--third_party/bazel/rules_haskell/tests/c2hs/src/Foo/Foo.chs6
-rw-r--r--third_party/bazel/rules_haskell/tests/cc_haskell_import/BUILD.bazel70
-rw-r--r--third_party/bazel/rules_haskell/tests/cc_haskell_import/LibA.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/cc_haskell_import/LibB.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/cc_haskell_import/cbits.c5
-rw-r--r--third_party/bazel/rules_haskell/tests/cc_haskell_import/main.c11
-rw-r--r--third_party/bazel/rules_haskell/tests/cc_haskell_import/python_add_one.py18
-rw-r--r--third_party/bazel/rules_haskell/tests/cpp_macro_conflict/BUILD.bazel36
-rw-r--r--third_party/bazel/rules_haskell/tests/cpp_macro_conflict/Main.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/cpp_macro_conflict/src/BS.hs1
-rw-r--r--third_party/bazel/rules_haskell/tests/data/BUILD.bazel15
-rw-r--r--third_party/bazel/rules_haskell/tests/data/ourclibrary.c5
-rw-r--r--third_party/bazel/rules_haskell/tests/encoding/BUILD.bazel21
-rw-r--r--third_party/bazel/rules_haskell/tests/encoding/Main.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/encoding/TH.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/encoding/unicode.txt1
-rw-r--r--third_party/bazel/rules_haskell/tests/external-haskell-repository/BUILD.bazel17
-rw-r--r--third_party/bazel/rules_haskell/tests/external-haskell-repository/Main.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/external-haskell-repository/workspace_dummy.bzl64
-rw-r--r--third_party/bazel/rules_haskell/tests/extra-source-files/BUILD.bazel44
-rw-r--r--third_party/bazel/rules_haskell/tests/extra-source-files/Foo.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/extra-source-files/FooTH.hs12
-rw-r--r--third_party/bazel/rules_haskell/tests/extra-source-files/Main.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/extra-source-files/file.txt1
-rw-r--r--third_party/bazel/rules_haskell/tests/extra-source-files/ld-options.txt0
-rw-r--r--third_party/bazel/rules_haskell/tests/failures/transitive-deps/BUILD.bazel66
-rw-r--r--third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibA.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibB.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibC.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibD.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/generated-modules/BUILD.bazel56
-rw-r--r--third_party/bazel/rules_haskell/tests/ghc.nix41
-rw-r--r--third_party/bazel/rules_haskell/tests/ghc/BUILD.bazel3
-rw-r--r--third_party/bazel/rules_haskell/tests/ghc/ghc.bzl19
-rw-r--r--third_party/bazel/rules_haskell/tests/hackage/BUILD.bazel34
-rw-r--r--third_party/bazel/rules_haskell/tests/haddock/BUILD.bazel73
-rw-r--r--third_party/bazel/rules_haskell/tests/haddock/Deep.hsc11
-rw-r--r--third_party/bazel/rules_haskell/tests/haddock/LibA.hs19
-rw-r--r--third_party/bazel/rules_haskell/tests/haddock/LibA/A.hs10
-rw-r--r--third_party/bazel/rules_haskell/tests/haddock/LibB.hs21
-rw-r--r--third_party/bazel/rules_haskell/tests/haddock/TH.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/haddock/header.h1
-rw-r--r--third_party/bazel/rules_haskell/tests/haddock/libC.nix47
-rw-r--r--third_party/bazel/rules_haskell/tests/haddock/unicode.txt1
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_doctest/BUILD.bazel73
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_doctest/Bar.hs13
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_doctest/Baz.hs14
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_doctest/Foo.hs14
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_doctest/Main.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_doctest/Quux.hsc11
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_lint/BUILD.bazel47
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_lint/Bar.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_lint/Foo.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_lint/Main.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_proto_library/BUILD.bazel78
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_proto_library/Bar.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_proto_library/address.proto11
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_proto_library/person.proto16
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_proto_library/stripped_address.proto10
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_proto_library/stripped_zip_code.proto7
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_proto_library/zip_code.proto7
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_test/BUILD.bazel29
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_test/Lib.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_test/Test.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_toolchain_library/BUILD.bazel26
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_toolchain_library/Bin.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/haskell_toolchain_library/Lib.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/hidden-modules/BUILD.bazel35
-rw-r--r--third_party/bazel/rules_haskell/tests/hidden-modules/lib-a/Bar.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/hidden-modules/lib-a/Foo.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/hidden-modules/lib-b/Foo.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/hidden-modules/lib-c/Baz.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/hs-boot/A.hs-boot.in2
-rw-r--r--third_party/bazel/rules_haskell/tests/hs-boot/A.hs.in8
-rw-r--r--third_party/bazel/rules_haskell/tests/hs-boot/BUILD.bazel48
-rw-r--r--third_party/bazel/rules_haskell/tests/hs-boot/MA.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/hs-boot/MA.hs-boot2
-rw-r--r--third_party/bazel/rules_haskell/tests/hs-boot/MB.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/hs-boot/Main.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/hs-boot/srcs/B.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/hsc/BUILD.bazel35
-rw-r--r--third_party/bazel/rules_haskell/tests/hsc/Bar.hsc6
-rw-r--r--third_party/bazel/rules_haskell/tests/hsc/Bar/Baz.hsc6
-rw-r--r--third_party/bazel/rules_haskell/tests/hsc/BinHsc.hsc1
-rw-r--r--third_party/bazel/rules_haskell/tests/hsc/Flags.hsc8
-rw-r--r--third_party/bazel/rules_haskell/tests/hsc/Foo.hsc8
-rw-r--r--third_party/bazel/rules_haskell/tests/hsc/Main.hs10
-rw-r--r--third_party/bazel/rules_haskell/tests/indirect-link/BUILD.bazel52
-rw-r--r--third_party/bazel/rules_haskell/tests/indirect-link/cbits/impl.c9
-rw-r--r--third_party/bazel/rules_haskell/tests/indirect-link/cbits/intf.c10
-rw-r--r--third_party/bazel/rules_haskell/tests/indirect-link/src/MyModule.hs11
-rw-r--r--third_party/bazel/rules_haskell/tests/indirect-link/test/Main.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/inline_tests.bzl90
-rw-r--r--third_party/bazel/rules_haskell/tests/java_classpath/BUILD.bazel17
-rw-r--r--third_party/bazel/rules_haskell/tests/java_classpath/Main.hs17
-rw-r--r--third_party/bazel/rules_haskell/tests/lhs/BUILD.bazel23
-rw-r--r--third_party/bazel/rules_haskell/tests/lhs/Lib.lhs4
-rw-r--r--third_party/bazel/rules_haskell/tests/lhs/Main.lhs6
-rw-r--r--third_party/bazel/rules_haskell/tests/library-deps/BUILD.bazel28
-rw-r--r--third_party/bazel/rules_haskell/tests/library-deps/Bin.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/library-deps/TestLib.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/library-deps/sublib/BUILD.bazel21
-rw-r--r--third_party/bazel/rules_haskell/tests/library-deps/sublib/TestSubLib.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/library-deps/sublib/sublib-c.c3
-rw-r--r--third_party/bazel/rules_haskell/tests/library-exports/BUILD.bazel41
-rw-r--r--third_party/bazel/rules_haskell/tests/library-exports/Bin.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/library-exports/TestLib.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/library-exports/TestSubLib.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/library-linkstatic-flag/BUILD.bazel112
-rw-r--r--third_party/bazel/rules_haskell/tests/library-linkstatic-flag/Lib.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/library-linkstatic-flag/Main.hs3
-rw-r--r--third_party/bazel/rules_haskell/tests/library-linkstatic-flag/get_library_files.bzl28
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-cbits/AddOne.hsc3
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-cbits/AddOne2.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-cbits/BUILD.bazel36
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-includes/BUILD.bazel29
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-includes/Lib.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-includes/b.h1
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-sysdeps/BUILD.bazel32
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-sysdeps/Lib.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-sysdeps/Main.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-sysincludes/BUILD.bazel86
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-sysincludes/IntLib.hsc15
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-sysincludes/Lib.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/library-with-sysincludes/TH.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/multi_repl/BUILD.bazel16
-rw-r--r--third_party/bazel/rules_haskell/tests/multi_repl/a/BUILD.bazel17
-rw-r--r--third_party/bazel/rules_haskell/tests/multi_repl/a/src/A/A.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/multi_repl/bc/BUILD.bazel30
-rw-r--r--third_party/bazel/rules_haskell/tests/multi_repl/bc/src/BC/B.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/multi_repl/bc/src/BC/C.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash-binary/BUILD.bazel15
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash-binary/Main.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash-binary/a/BUILD.bazel12
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash-binary/a/Foo.hs3
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash-binary/b/BUILD.bazel12
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash-binary/b/Baz.hs3
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash/BUILD.bazel24
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash/Baz.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash/Foo.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash/sublib/BUILD.bazel13
-rw-r--r--third_party/bazel/rules_haskell/tests/package-id-clash/sublib/Bar.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/package-name/BUILD.bazel28
-rw-r--r--third_party/bazel/rules_haskell/tests/package-name/Lib.hs8
-rw-r--r--third_party/bazel/rules_haskell/tests/package-name/Main.hs33
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-flags/BUILD.bazel45
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-flags/CompilerFlags.hs10
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-flags/ReplFlags.hs17
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-name-conflicts/BUILD.bazel15
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-name-conflicts/PreludeShadowing.hs12
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-targets/BUILD.bazel81
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-targets/Bad.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-targets/Chs.chs6
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-targets/Foo.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-targets/Hsc.hsc8
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-targets/Quux.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-targets/QuuxLib.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-targets/src/Bar.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/repl-targets/src/Baz.hsc4
-rw-r--r--third_party/bazel/rules_haskell/tests/rule_test_exe.bzl14
-rwxr-xr-xthird_party/bazel/rules_haskell/tests/run-start-script.sh30
-rwxr-xr-xthird_party/bazel/rules_haskell/tests/scripts/exec.sh4
-rw-r--r--third_party/bazel/rules_haskell/tests/textual-hdrs/BUILD.bazel17
-rw-r--r--third_party/bazel/rules_haskell/tests/textual-hdrs/Main.hs5
-rw-r--r--third_party/bazel/rules_haskell/tests/textual-hdrs/include/main_definition.h2
-rw-r--r--third_party/bazel/rules_haskell/tests/two-libs/BUILD.bazel42
-rw-r--r--third_party/bazel/rules_haskell/tests/two-libs/Main.hs6
-rw-r--r--third_party/bazel/rules_haskell/tests/two-libs/One.hs4
-rw-r--r--third_party/bazel/rules_haskell/tests/two-libs/Two.hs14
-rw-r--r--third_party/bazel/rules_haskell/tests/unit-tests/BUILD.bazel125
-rw-r--r--third_party/bazel/rules_haskell/tests/unit-tests/tests.bzl83
-rw-r--r--third_party/bazel/rules_haskell/tests/version-macros/BUILD.bazel59
-rw-r--r--third_party/bazel/rules_haskell/tests/version-macros/C2hsLib.chs44
-rw-r--r--third_party/bazel/rules_haskell/tests/version-macros/HsLib.hs46
-rw-r--r--third_party/bazel/rules_haskell/tests/version-macros/HscLib.hsc44
-rw-r--r--third_party/bazel/rules_haskell/tests/version-macros/Main.hs9
-rw-r--r--third_party/bazel/rules_haskell/tests/version-macros/MainC2hs.hs7
-rw-r--r--third_party/bazel/rules_haskell/tests/version-macros/VersionedLib.hs1
236 files changed, 4522 insertions, 0 deletions
diff --git a/third_party/bazel/rules_haskell/tests/BUILD.bazel b/third_party/bazel/rules_haskell/tests/BUILD.bazel
new file mode 100644
index 000000000000..276db5546332
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/BUILD.bazel
@@ -0,0 +1,322 @@
+load(":inline_tests.bzl", "sh_inline_test")
+load("@bazel_tools//tools/build_rules:test_rules.bzl", "rule_test")
+load("//tests:rule_test_exe.bzl", "rule_test_exe")
+load(
+    "@io_tweag_rules_haskell//haskell:c2hs.bzl",
+    "c2hs_toolchain",
+)
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_binary",
+    "haskell_doctest_toolchain",
+    "haskell_proto_toolchain",
+    "haskell_test",
+    "haskell_toolchain",
+)
+load(
+    "//:constants.bzl",
+    "test_ghc_version",
+)
+
+package(default_testonly = 1)
+
+haskell_doctest_toolchain(
+    name = "doctest-toolchain",
+    doctest = "@hackage-doctest//:bin",
+    tags = ["requires_doctest"],
+)
+
+# This toolchain is morally testonly. However, that would break our
+# tests of haskell_library_rules: aspects of non-testonly
+# proto_library rules (from com_google_protobuf) can't themselves be
+# testonly.
+
+haskell_proto_toolchain(
+    name = "protobuf-toolchain",
+    testonly = 0,
+    plugin = "@hackage-proto-lens-protoc//:bin/proto-lens-protoc",
+    protoc = "@com_google_protobuf//:protoc",
+    tags = ["requires_hackage"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:bytestring",
+        "//tests/hackage:containers",
+        "//tests/hackage:deepseq",
+        "//tests/hackage:mtl",
+        "//tests/hackage:text",
+        "@hackage//:data-default-class",
+        "@hackage//:lens-family",
+        "@hackage//:lens-family-core",
+        "@hackage//:lens-labels",
+        "@hackage//:proto-lens",
+    ],
+)
+
+c2hs_toolchain(
+    name = "c2hs-toolchain",
+    c2hs = "@hackage-c2hs//:bin",
+    tags = ["requires_c2hs"],
+)
+
+rule_test_exe(
+    name = "test-binary-simple",
+    size = "small",
+    generates = ["binary-simple"],
+    rule = "//tests/binary-simple",
+)
+
+rule_test_exe(
+    name = "test-binary-custom-main",
+    size = "small",
+    generates = ["binary-custom-main"],
+    rule = "//tests/binary-custom-main",
+)
+
+rule_test(
+    name = "test-binary-with-lib",
+    size = "small",
+    generates = ["binary-with-lib"],
+    rule = "//tests/binary-with-lib",
+)
+
+rule_test(
+    name = "test-binary-with-prebuilt",
+    size = "small",
+    generates = ["binary-with-prebuilt"],
+    rule = "//tests/binary-with-prebuilt",
+    tags = ["requires_hackage"],
+)
+
+rule_test(
+    name = "test-binary-with-main",
+    size = "small",
+    generates = ["binary-with-main"],
+    rule = "//tests/binary-with-main",
+)
+
+rule_test(
+    name = "test-binary-with-sysdeps",
+    size = "small",
+    generates = ["binary-with-sysdeps"],
+    rule = "//tests/binary-with-sysdeps",
+    tags = ["requires_zlib"],
+)
+
+sh_test(
+    name = "test-binary-with-data",
+    size = "small",
+    srcs = ["//tests/binary-with-data"],
+    args = ["$(location //tests/binary-with-data:bin1)"],
+    data = ["//tests/binary-with-data:bin1"],
+    tags = ["requires_hackage"],
+)
+
+rule_test(
+    name = "test-library-deps",
+    size = "small",
+    generates = select({
+        "@bazel_tools//src/conditions:darwin": [
+            "libHStestsZSlibrary-depsZSlibrary-deps-ghc{}.dylib".format(test_ghc_version),
+            "libHStestsZSlibrary-depsZSlibrary-deps.a",
+        ],
+        "@bazel_tools//src/conditions:windows": [
+            "libHStestsZSlibrary-depsZSlibrary-deps-ghc{}.dll".format(test_ghc_version),
+            "libHStestsZSlibrary-depsZSlibrary-deps.a",
+        ],
+        "//conditions:default": [
+            "libHStestsZSlibrary-depsZSlibrary-deps-ghc{}.so".format(test_ghc_version),
+            "libHStestsZSlibrary-depsZSlibrary-deps.a",
+        ],
+    }),
+    rule = "//tests/library-deps",
+)
+
+rule_test(
+    name = "test-hsc",
+    size = "small",
+    generates = ["hsc"],
+    rule = "//tests/hsc",
+)
+
+rule_test(
+    name = "test-haddock",
+    size = "small",
+    generates = [
+        "haddock/index",
+        "haddock/testsZShaddockZShaddock-lib-a",
+        "haddock/testsZShaddockZShaddock-lib-b",
+        "haddock/testsZShaddockZShaddock-lib-deep",
+    ],
+    rule = "//tests/haddock",
+    tags = ["requires_hackage"],
+)
+
+rule_test(
+    name = "test-haskell_lint-library",
+    size = "small",
+    generates = [
+        "lint-log-lib-b",
+    ],
+    rule = "//tests/haskell_lint:lint-lib-b",
+)
+
+rule_test(
+    name = "test-haskell_lint-binary",
+    size = "small",
+    generates = [
+        "lint-log-bin",
+    ],
+    rule = "//tests/haskell_lint:lint-bin",
+)
+
+rule_test(
+    name = "test-haskell_doctest",
+    size = "small",
+    generates = [
+        "doctest-log-doctest-lib-lib-b",
+    ],
+    rule = "//tests/haskell_doctest:doctest-lib",
+    tags = ["requires_doctest"],
+)
+
+rule_test(
+    name = "test-haskell_test",
+    size = "small",
+    generates = ["haskell_test"],
+    rule = "//tests/haskell_test:haskell_test",
+)
+
+rule_test(
+    name = "test-java_classpath",
+    size = "small",
+    generates = ["java_classpath"],
+    rule = "//tests/java_classpath",
+)
+
+rule_test(
+    name = "test-cc_haskell_import-cc-link",
+    size = "small",
+    generates = ["cc-bin"],
+    rule = "//tests/cc_haskell_import:cc-bin",
+    tags = ["requires_threaded_rts"],
+)
+
+sh_test(
+    name = "test-cc_haskell_import_python",
+    size = "small",
+    srcs = ["scripts/exec.sh"],
+    args = ["tests/cc_haskell_import/python_add_one"],
+    data = [
+        "//tests/cc_haskell_import:python_add_one",
+        "@bazel_tools//tools/bash/runfiles",
+    ],
+    tags = ["requires_threaded_rts"],
+)
+
+sh_inline_test(
+    name = "test-haskell_binary-with-link-flags",
+    size = "small",
+    args = ["$(location //tests/binary-with-link-flags:binary-with-link-flags)"],
+    data = ["//tests/binary-with-link-flags"],
+    script = """\
+set -e
+
+# Fails if executable was linked without -threaded flag.
+$1 +RTS -N
+""",
+)
+
+rule_test(
+    name = "test-lhs",
+    size = "small",
+    generates = ["lhs-bin"],
+    rule = "//tests/lhs:lhs-bin",
+)
+
+rule_test(
+    name = "test-hs-boot",
+    size = "small",
+    generates = ["hs-boot"],
+    rule = "//tests/hs-boot:hs-boot",
+)
+
+rule_test(
+    name = "test-textual-hdrs",
+    size = "small",
+    generates = ["textual-hdrs"],
+    rule = "//tests/textual-hdrs:textual-hdrs",
+)
+
+rule_test(
+    name = "test-two-libs",
+    size = "small",
+    generates = ["two-libs"],
+    rule = "//tests/two-libs:two-libs",
+)
+
+genrule(
+    name = "run-bin-with-lib",
+    outs = ["dummy"],
+    cmd = """sh -c '
+    set -e
+    $(location //tests/binary-with-lib:binary-with-lib)
+    touch $(location dummy)
+'""",
+    tools = ["//tests/binary-with-lib"],
+)
+
+rule_test(
+    name = "test-run-bin-with-lib",
+    size = "small",
+    generates = ["dummy"],
+    rule = "//tests:run-bin-with-lib",
+)
+
+genrule(
+    name = "run-bin-with-lib-dynamic",
+    outs = ["dyn-dummy"],
+    cmd = """sh -c '
+    set -e
+    $(location //tests/binary-with-lib-dynamic:binary-with-lib-dynamic)
+    touch $(location dyn-dummy)
+'""",
+    tools = ["//tests/binary-with-lib-dynamic"],
+)
+
+rule_test(
+    name = "test-run-bin-with-lib-dynamic",
+    size = "small",
+    generates = ["dyn-dummy"],
+    rule = "//tests:run-bin-with-lib-dynamic",
+)
+
+genrule(
+    name = "run-bin-with-c-lib",
+    outs = ["c-dummy"],
+    cmd = """sh -c '
+    set -e
+    $(location //tests/c-compiles)
+    touch $(location c-dummy)
+'""",
+    tools = ["//tests/c-compiles"],
+)
+
+rule_test(
+    name = "test-run-bin-with-c-lib",
+    size = "small",
+    generates = ["c-dummy"],
+    rule = "//tests:run-bin-with-c-lib",
+)
+
+# This is the test runner
+haskell_binary(
+    name = "run-tests",
+    srcs = ["RunTests.hs"],
+    tags = ["requires_hackage"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:process",
+        "@hackage//:hspec",
+        "@hackage//:hspec-core",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/RunTests.hs b/third_party/bazel/rules_haskell/tests/RunTests.hs
new file mode 100644
index 000000000000..b6218bbcefc2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/RunTests.hs
@@ -0,0 +1,155 @@
+{-# OPTIONS -Wall #-}
+
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+import Data.Foldable (for_)
+import Data.List (isInfixOf, sort)
+import System.Exit (ExitCode(..))
+
+import qualified System.Process as Process
+import Test.Hspec.Core.Spec (SpecM)
+import Test.Hspec (hspec, it, describe, runIO, shouldSatisfy, expectationFailure)
+
+main :: IO ()
+main = hspec $ do
+  it "bazel lint" $ do
+    assertSuccess (bazel ["run", "//:buildifier"])
+
+  it "bazel test" $ do
+    assertSuccess (bazel ["test", "//...", "--build_tests_only"])
+
+  it "haddock links" $ do
+    -- Test haddock links
+    -- All haddock tests are stored inside //tests/haddock
+    -- Temporaries files appears inside /doc-.... outputs and are ignored
+
+    -- the copy / chmod is here to workaround the fact that
+    -- linkchecker is dropping privileges to "nobody" user if called
+    -- from root, which is the case on CI.
+    assertSuccess (safeShell
+      [ "bazel build --config=ci //tests/haddock/..."
+      , "pwd=$(pwd)"
+      , "cd $(mktemp -d)"
+      , "cp -r $pwd/bazel-ci-bin/tests/haddock ."
+      , "chmod -R o+r ."
+      , "linkchecker . --ignore-url=/doc-"
+      ])
+
+  it "bazel test prof" $ do
+    assertSuccess (bazel ["test", "-c", "dbg", "//...", "--build_tests_only"])
+
+  describe "repl" $ do
+    it "for libraries" $ do
+      assertSuccess (bazel ["run", "//tests/repl-targets:hs-lib@repl", "--", "-ignore-dot-ghci", "-e", "show (foo 10) ++ bar ++ baz ++ gen"])
+      assertSuccess (bazel ["run", "//tests/repl-targets:hs-lib-bad@repl", "--", "-ignore-dot-ghci", "-e", "1 + 2"])
+
+    it "for binaries" $ do
+      assertSuccess (bazel ["run", "//tests/repl-targets:hs-bin@repl", "--", "-ignore-dot-ghci", "-e", ":main"])
+
+      assertSuccess (bazel ["run", "//tests/binary-indirect-cbits:binary-indirect-cbits@repl", "--", "-ignore-dot-ghci", "-e", ":main"])
+
+    -- Test `compiler_flags` from toolchain and rule for REPL
+    it "compiler flags" $ do
+      assertSuccess (bazel ["run", "//tests/repl-flags:compiler_flags@repl", "--", "-ignore-dot-ghci", "-e", ":main"])
+
+    -- Test `repl_ghci_args` from toolchain and rule for REPL
+    it "repl flags" $ do
+      assertSuccess (bazel ["run", "//tests/repl-flags:repl_flags@repl", "--", "-ignore-dot-ghci", "-e", "foo"])
+
+  describe "multi_repl" $ do
+    it "loads transitive library dependencies" $ do
+      let p' (stdout, _stderr) = lines stdout == ["tests/multi_repl/bc/src/BC/C.hs"]
+      outputSatisfy p' (bazel ["run", "//tests/multi_repl:c_only_repl", "--", "-ignore-dot-ghci", "-e", ":show targets"])
+    it "loads transitive source dependencies" $ do
+      let p' (stdout, _stderr) = sort (lines stdout) == ["tests/multi_repl/a/src/A/A.hs","tests/multi_repl/bc/src/BC/B.hs","tests/multi_repl/bc/src/BC/C.hs"]
+      outputSatisfy p' (bazel ["run", "//tests/multi_repl:c_multi_repl", "--", "-ignore-dot-ghci", "-e", ":show targets"])
+
+  it "startup script" $ do
+    assertSuccess (safeShell ["./tests/run-start-script.sh"])
+
+  describe "failures" $ do
+    all_failure_tests <- bazelQuery "kind(rule, //tests/failures/...) intersect attr('tags', 'manual', //tests/failures/...)"
+
+    for_ all_failure_tests $ \test -> do
+      it test $ do
+        assertFailure (bazel ["build", "test"])
+
+  -- Test that the repl still works if we shadow some Prelude functions
+  it "repl name shadowing" $ do
+    let p (stdout, stderr) = not $ any ("error" `isInfixOf`) [stdout, stderr]
+    outputSatisfy p (bazel ["run", "//tests/repl-name-conflicts:lib@repl", "--", "-ignore-dot-ghci", "-e", "stdin"])
+
+  it "bazel test examples" $ do
+    assertSuccess (bazel ["test", "@io_tweag_rules_haskell_examples//..."])
+
+  it "bazel test tutorial" $ do
+    assertSuccess (bazel ["test", "@io_tweag_rules_haskell_tutorial//..."])
+
+-- * Bazel commands
+
+-- | Returns a bazel command line suitable for CI
+-- This should be called with the action as first item of the list. e.g 'bazel ["build", "//..."]'.
+bazel :: [String] -> Process.CreateProcess
+-- Note: --config=ci is intercalated between the action and the list
+-- of arguments. It should appears after the action, but before any
+-- @--@ following argument.
+bazel (command:args) = Process.proc "bazel" (command:"--config=ci":args)
+bazel [] = Process.proc "bazel" []
+
+-- | Runs a bazel query and return the list of matching targets
+bazelQuery :: String -> SpecM a [String]
+bazelQuery q = lines <$> runIO (Process.readProcess "bazel" ["query", q] "")
+
+-- * Action helpers
+
+-- | Ensure that @(stdout, stderr)@ of the command satisfies a predicate
+outputSatisfy
+  :: ((String, String) -> Bool)
+  -> Process.CreateProcess
+  -> IO ()
+outputSatisfy predicate cmd = do
+  (exitCode, stdout, stderr) <- Process.readCreateProcessWithExitCode cmd ""
+
+  case exitCode of
+    ExitSuccess -> (stdout, stderr) `shouldSatisfy` predicate
+    ExitFailure _ -> expectationFailure (formatOutput exitCode stdout stderr)
+
+-- | The command must success
+assertSuccess :: Process.CreateProcess -> IO ()
+assertSuccess = outputSatisfy (const True)
+
+-- | The command must fail
+assertFailure :: Process.CreateProcess -> IO ()
+assertFailure cmd = do
+  (exitCode, stdout, stderr) <- Process.readCreateProcessWithExitCode cmd ""
+
+  case exitCode of
+    ExitFailure _ -> pure ()
+    ExitSuccess -> expectationFailure ("Unexpected success of a failure test with output:\n" ++ formatOutput exitCode stdout stderr)
+
+-- | Execute in a sub shell the list of command
+-- This will fail if any of the command in the list fail
+safeShell :: [String] -> Process.CreateProcess
+safeShell l = Process.shell (unlines ("set -e":l))
+
+-- * Formatting helpers
+
+formatOutput :: ExitCode -> String -> String -> String
+formatOutput exitcode stdout stderr =
+  let
+    header = replicate 20 '-'
+    headerLarge = replicate 20 '='
+
+  in unlines [
+      headerLarge
+    , "Exit Code: " <> show exitcode
+    , headerLarge
+    , "Standard Output"
+    , header
+    , stdout
+    , headerLarge
+    , "Error Output"
+    , header
+    , stderr
+    , header]
diff --git a/third_party/bazel/rules_haskell/tests/binary-custom-main/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-custom-main/BUILD.bazel
new file mode 100644
index 000000000000..c749b85eaac4
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-custom-main/BUILD.bazel
@@ -0,0 +1,17 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "binary-custom-main",
+    srcs = ["Main.hs"],
+    expected_covered_expressions_percentage = 50,
+    tags = [
+        "coverage-compatible",
+    ],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-custom-main/Main.hs b/third_party/bazel/rules_haskell/tests/binary-custom-main/Main.hs
new file mode 100644
index 000000000000..c8dbf9fbdb9e
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-custom-main/Main.hs
@@ -0,0 +1,4 @@
+module Main (main) where
+
+main :: IO ()
+main = return ()
diff --git a/third_party/bazel/rules_haskell/tests/binary-dynamic/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-dynamic/BUILD.bazel
new file mode 100644
index 000000000000..765963f19fe0
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-dynamic/BUILD.bazel
@@ -0,0 +1,14 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "binary-dynamic",
+    srcs = ["Main.hs"],
+    linkstatic = False,
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-dynamic/Main.hs b/third_party/bazel/rules_haskell/tests/binary-dynamic/Main.hs
new file mode 100644
index 000000000000..2048dbdecd9a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-dynamic/Main.hs
@@ -0,0 +1,3 @@
+module Main where
+
+main = putStrLn "hello world"
diff --git a/third_party/bazel/rules_haskell/tests/binary-exe-path/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-exe-path/BUILD.bazel
new file mode 100644
index 000000000000..0cb617a50877
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-exe-path/BUILD.bazel
@@ -0,0 +1,13 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "binary-exe-path",
+    srcs = ["Main.hs"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-exe-path/Main.hs b/third_party/bazel/rules_haskell/tests/binary-exe-path/Main.hs
new file mode 100644
index 000000000000..6a02bb9fc8ec
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-exe-path/Main.hs
@@ -0,0 +1,5 @@
+module Main where
+
+import System.Environment
+
+main = getExecutablePath >>= putStrLn
diff --git a/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/BUILD.bazel
new file mode 100644
index 000000000000..6668f230e508
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/BUILD.bazel
@@ -0,0 +1,36 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_binary",
+    "haskell_library",
+)
+
+haskell_binary(
+    name = "binary-indirect-cbits",
+    srcs = ["Main.hs"],
+    linkstatic = False,
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/library-with-cbits",
+    ],
+)
+
+haskell_binary(
+    name = "binary-indirect-cbits-partially-static",
+    srcs = ["Main.hs"],
+    linkstatic = False,
+    deps = [
+        "//tests/hackage:base",
+        "//tests/library-with-cbits:library-with-cbits-static",
+    ],
+)
+
+haskell_binary(
+    name = "binary-indirect-cbits-fully-static",
+    srcs = ["Main.hs"],
+    linkstatic = True,
+    deps = [
+        "//tests/hackage:base",
+        "//tests/library-with-cbits:library-with-cbits-static",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Main.hs b/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Main.hs
new file mode 100644
index 000000000000..c9d7037e23c2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Main.hs
@@ -0,0 +1,5 @@
+import AddOne
+
+main :: IO ()
+main = do
+  putStrLn $ show $ addOne 2
diff --git a/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Wrapper.hs b/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Wrapper.hs
new file mode 100644
index 000000000000..00b6be28f405
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Wrapper.hs
@@ -0,0 +1,5 @@
+module Wrapper
+  ( module AddOne
+  ) where
+
+import AddOne
diff --git a/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Wrapper2.hs b/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Wrapper2.hs
new file mode 100644
index 000000000000..a39233a84271
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-indirect-cbits/Wrapper2.hs
@@ -0,0 +1,9 @@
+module Wrapper2
+  ( module AddOne
+  , addOne2
+  ) where
+
+import AddOne
+import qualified Wrapper
+
+addOne2 = Wrapper.addOne
diff --git a/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/BUILD.bazel
new file mode 100644
index 000000000000..4e8e7cfa6684
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/BUILD.bazel
@@ -0,0 +1,118 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+load("//tests:inline_tests.bzl", "sh_inline_test")
+
+# test whether `linkstatic` works as expected
+package(default_testonly = 1)
+
+cc_library(
+    name = "c-lib",
+    srcs = ["c-lib.c"],
+)
+
+haskell_library(
+    name = "HsLib",
+    srcs = ["HsLib.hs"],
+    deps = [
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_test(
+    name = "binary-static",
+    srcs = ["Main.hs"],
+    linkstatic = True,
+    deps = [
+        ":HsLib",
+        ":c-lib",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_test(
+    name = "binary-dynamic",
+    srcs = ["Main.hs"],
+    linkstatic = False,
+    deps = [
+        ":HsLib",
+        ":c-lib",
+        "//tests/hackage:base",
+    ],
+)
+
+config_setting(
+    name = "debug_build",
+    values = {
+        "compilation_mode": "dbg",
+    },
+)
+
+# Ensure that linkstatic=True only links to static library targets.
+sh_inline_test(
+    name = "test-binary-static-symbols",
+    size = "small",
+    args = [
+        "$(rootpath :binary-static)",
+    ],
+    data = [
+        ":binary-static",
+    ],
+    script = """
+    set -eo pipefail
+    binary="$1"
+    # Symbols are prefixed with underscore on MacOS but not on Linux.
+    if nm -u "$binary" | grep -q "\<_\?value"; then
+        echo "C library dependency not linked statically: ${binary}"
+        exit 1
+    fi
+    if nm -u "$binary" | grep -q HsLib_value_closure; then
+        echo "Haskell library dependency not linked statically ${binary}"
+        exit 1
+    fi
+    """,
+)
+
+# Ensure that linkstatic=False only links to dynamic library targets.
+sh_inline_test(
+    name = "test-binary-dynamic-symbols",
+    size = "small",
+    args = [
+        "$(rootpath :binary-dynamic)",
+    ] + select({
+        ":debug_build": ["dbg"],
+        "//conditions:default": ["rls"],
+    }),
+    data = [
+        ":binary-dynamic",
+    ],
+    script = """
+    set -eo pipefail
+    binary="$1"
+    mode="$2"
+    if [[ $mode = dbg ]]; then
+        # Skip test in debug builds. Debug mode forces static linking.
+        exit 0
+    fi
+    # Symbols are prefixed with underscore on MacOS but not on Linux.
+    if ! nm -u "$binary" | grep -q "\<_\?value"; then
+        echo "C library dependency not linked dynamically"
+        exit 1
+    fi
+    if ! nm -u "$binary" | grep -q HsLib_value_closure; then
+        echo "Haskell library dependency not linked dynamically"
+        exit 1
+    fi
+    """,
+)
+
+test_suite(
+    name = "binary-linkstatic-flag",
+    tests = [
+        ":test-binary-dynamic-symbols",
+        ":test-binary-static-symbols",
+    ],
+    visibility = ["//visibility:public"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/HsLib.hs b/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/HsLib.hs
new file mode 100644
index 000000000000..68271826d7ee
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/HsLib.hs
@@ -0,0 +1,4 @@
+module HsLib (value) where
+
+value :: Int
+value = 13
diff --git a/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/Main.hs b/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/Main.hs
new file mode 100644
index 000000000000..7836c2220277
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/Main.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+module Main (main) where
+
+import qualified HsLib
+
+foreign import ccall "value" value :: Int
+
+main = print $ HsLib.value + value
diff --git a/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/c-lib.c b/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/c-lib.c
new file mode 100644
index 000000000000..9a1e526017ec
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-linkstatic-flag/c-lib.c
@@ -0,0 +1 @@
+int value() { return 29; }
diff --git a/third_party/bazel/rules_haskell/tests/binary-simple/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-simple/BUILD.bazel
new file mode 100644
index 000000000000..d44d395fb8c7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-simple/BUILD.bazel
@@ -0,0 +1,15 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "binary-simple",
+    srcs = ["Main.hs"],
+    expected_covered_expressions_percentage = 100,
+    tags = ["coverage-compatible"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-simple/Main.hs b/third_party/bazel/rules_haskell/tests/binary-simple/Main.hs
new file mode 100644
index 000000000000..2048dbdecd9a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-simple/Main.hs
@@ -0,0 +1,3 @@
+module Main where
+
+main = putStrLn "hello world"
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-compiler-flags/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-compiler-flags/BUILD.bazel
new file mode 100644
index 000000000000..727d8447ab4b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-compiler-flags/BUILD.bazel
@@ -0,0 +1,18 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "binary-with-compiler-flags",
+    srcs = ["Main.hs"],
+    # Flags that require -threaded, which we should get from the toolchain's
+    # compiler_flags. Include spaces to validate proper quoting:
+    compiler_flags = [
+        "-with-rtsopts=-N2 -qg -I0 -n2m -A128m",
+        "-XLambdaCase",
+    ],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-compiler-flags/Main.hs b/third_party/bazel/rules_haskell/tests/binary-with-compiler-flags/Main.hs
new file mode 100644
index 000000000000..b95db17f76da
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-compiler-flags/Main.hs
@@ -0,0 +1,13 @@
+-- Expects to pull -XStandaloneDeriving from the default compiler flags.
+module Main (main) where
+
+data Foo = Foo
+deriving instance Show Foo
+
+-- Expects -XLambdaCase to be passed via the 'compiler_flags' rule attribute.
+dummyId :: Foo -> Foo
+dummyId = \case
+  Foo -> Foo
+
+main :: IO ()
+main = print $ dummyId Foo
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-data/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-data/BUILD.bazel
new file mode 100644
index 000000000000..200498f64018
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-data/BUILD.bazel
@@ -0,0 +1,31 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+# XXX: on Windows those tests need `--experimental_enable_runfiles` to succeed
+# XXX: see: https://github.com/tweag/rules_haskell/issues/647#issuecomment-459001362
+
+haskell_test(
+    name = "bin1",
+    srcs = ["bin1.hs"],
+    # Regular file input:
+    data = ["bin1-input"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_test(
+    name = "binary-with-data",
+    srcs = ["bin2.hs"],
+    args = ["$(location :bin1)"],
+    data = [":bin1"],
+    tags = ["requires_hackage"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:process",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-data/bin1-input b/third_party/bazel/rules_haskell/tests/binary-with-data/bin1-input
new file mode 100644
index 000000000000..12f00e90b6ef
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-data/bin1-input
@@ -0,0 +1 @@
+contents
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-data/bin1.hs b/third_party/bazel/rules_haskell/tests/binary-with-data/bin1.hs
new file mode 100644
index 000000000000..309f6ecd5641
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-data/bin1.hs
@@ -0,0 +1,9 @@
+module Main where
+
+import Control.Monad (unless)
+
+main :: IO ()
+main = do
+    contents <- readFile "tests/binary-with-data/bin1-input"
+    unless (contents == "contents\n")
+      $ error $ "Incorrect input; got " ++ show contents
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-data/bin2.hs b/third_party/bazel/rules_haskell/tests/binary-with-data/bin2.hs
new file mode 100644
index 000000000000..31d2404a6a9d
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-data/bin2.hs
@@ -0,0 +1,8 @@
+module Main where
+
+import System.Process (callProcess)
+import System.Environment (getArgs)
+
+main = do
+  [arg] <- getArgs
+  callProcess arg []
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-import/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-import/BUILD.bazel
new file mode 100644
index 000000000000..3b920d37c41a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-import/BUILD.bazel
@@ -0,0 +1,26 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "lib",
+    srcs = ["src/Lib.hs"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:transformers",
+    ],
+)
+
+haskell_test(
+    name = "binary-with-import",
+    srcs = ["Main.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":lib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-import/Main.hs b/third_party/bazel/rules_haskell/tests/binary-with-import/Main.hs
new file mode 100644
index 000000000000..1bded5e8e55b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-import/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import Lib (printValue)
+
+main :: IO ()
+main = printValue
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-import/src/Lib.hs b/third_party/bazel/rules_haskell/tests/binary-with-import/src/Lib.hs
new file mode 100644
index 000000000000..d384e73e6337
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-import/src/Lib.hs
@@ -0,0 +1,13 @@
+module Lib (printValue) where
+
+import Control.Monad (void)
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.Except (ExceptT, runExceptT)
+
+getValue :: Monad m => ExceptT e m Int
+getValue = pure 42
+
+printValue :: IO ()
+printValue = void $ runExceptT $ do
+  value <- getValue
+  lift $ print value
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/BUILD.bazel
new file mode 100644
index 000000000000..195288882b1a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/BUILD.bazel
@@ -0,0 +1,27 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "hs-lib",
+    srcs = ["HsLib.hs"],
+    tags = ["requires_zlib"],
+    deps = [
+        "//tests/hackage:base",
+        "@zlib.dev//:zlib",
+    ],
+)
+
+haskell_test(
+    name = "binary-with-indirect-sysdeps",
+    srcs = ["Main.hs"],
+    tags = ["requires_zlib"],
+    deps = [
+        ":hs-lib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/HsLib.hs b/third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/HsLib.hs
new file mode 100644
index 000000000000..1077606e31e2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/HsLib.hs
@@ -0,0 +1,8 @@
+module HsLib where
+
+import Foreign.Ptr
+import Foreign.C.Types
+
+foreign import ccall crc32 :: CLong -> Ptr () -> CInt -> IO ()
+
+test = crc32 0 nullPtr 0
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/Main.hs b/third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/Main.hs
new file mode 100644
index 000000000000..9c92b34c8a38
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-indirect-sysdeps/Main.hs
@@ -0,0 +1,5 @@
+module Main where
+
+import HsLib (test)
+
+main = test
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/BUILD.bazel
new file mode 100644
index 000000000000..6be2db88b732
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/BUILD.bazel
@@ -0,0 +1,25 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "lib",
+    srcs = glob(["src/*.hs"]),
+    linkstatic = False,
+    src_strip_prefix = "src",
+)
+
+haskell_test(
+    name = "binary-with-lib-dynamic",
+    srcs = ["Main.hs"],
+    linkstatic = False,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":lib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/Main.hs b/third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/Main.hs
new file mode 100644
index 000000000000..d88f7c93d66a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/Main.hs
@@ -0,0 +1,7 @@
+module Main where
+
+import Control.Monad (unless)
+import Lib           (value)
+
+main = unless (value == 42)
+    $ error $ "Incorrect lib value. Got " <> show value
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/src/Lib.hs b/third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/src/Lib.hs
new file mode 100644
index 000000000000..0521761f3572
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-lib-dynamic/src/Lib.hs
@@ -0,0 +1,5 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Lib (value) where
+
+value = 42
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-lib/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-lib/BUILD.bazel
new file mode 100644
index 000000000000..196d551305a9
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-lib/BUILD.bazel
@@ -0,0 +1,27 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "lib",
+    srcs = glob(["src/*.hs"]),
+    src_strip_prefix = "src",
+    deps = [
+        "//tests/hackage:template-haskell",
+    ],
+)
+
+haskell_test(
+    name = "binary-with-lib",
+    srcs = ["Main.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":lib",
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-lib/Main.hs b/third_party/bazel/rules_haskell/tests/binary-with-lib/Main.hs
new file mode 100644
index 000000000000..19ad3dd58ea0
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-lib/Main.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE TemplateHaskell #-}
+module Main where
+
+import Control.Monad (unless)
+import Lib           (value)
+import Language.Haskell.TH
+
+val = $(value)
+
+main = unless (val == 42)
+    $ error $ "Incorrect lib value. Got " <> show val
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-lib/src/Lib.hs b/third_party/bazel/rules_haskell/tests/binary-with-lib/src/Lib.hs
new file mode 100644
index 000000000000..e78d2ef77f1b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-lib/src/Lib.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Lib (value) where
+
+import Language.Haskell.TH
+
+value = [|42|]
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-link-flags/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-link-flags/BUILD.bazel
new file mode 100644
index 000000000000..cf2aba515b69
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-link-flags/BUILD.bazel
@@ -0,0 +1,17 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(
+    default_testonly = 1,
+    default_visibility = ["//visibility:public"],
+)
+
+haskell_test(
+    name = "binary-with-link-flags",
+    srcs = ["Main.hs"],
+    compiler_flags = ["-threaded"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-link-flags/Main.hs b/third_party/bazel/rules_haskell/tests/binary-with-link-flags/Main.hs
new file mode 100644
index 000000000000..de106fe48f9a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-link-flags/Main.hs
@@ -0,0 +1,3 @@
+module Main where
+
+main = return ()
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-main/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-main/BUILD.bazel
new file mode 100644
index 000000000000..e1243260c2ab
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-main/BUILD.bazel
@@ -0,0 +1,14 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "binary-with-main",
+    srcs = ["MainIsHere.hs"],
+    main_function = "MainIsHere.this",
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-main/MainIsHere.hs b/third_party/bazel/rules_haskell/tests/binary-with-main/MainIsHere.hs
new file mode 100644
index 000000000000..5e8d38ba70df
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-main/MainIsHere.hs
@@ -0,0 +1,4 @@
+module MainIsHere (this) where
+
+this :: IO ()
+this = return ()
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-plugin/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-plugin/BUILD.bazel
new file mode 100644
index 000000000000..f3a8e3f4d689
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-plugin/BUILD.bazel
@@ -0,0 +1,53 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "ghc_plugin",
+    "haskell_library",
+    "haskell_test",
+    "haskell_toolchain_library",
+)
+
+package(default_testonly = 1)
+
+config_setting(
+    name = "debug_build",
+    values = {
+        "compilation_mode": "dbg",
+    },
+)
+
+haskell_toolchain_library(name = "base")
+
+haskell_toolchain_library(name = "ghc")
+
+haskell_toolchain_library(name = "process")
+
+haskell_library(
+    name = "plugin-lib",
+    srcs = ["Plugin.hs"],
+    deps = [
+        ":base",
+        ":ghc",
+        ":process",
+    ],
+)
+
+ghc_plugin(
+    name = "plugin",
+    args = ["$(location //tests/binary-simple)"],
+    module = "Plugin",
+    tools = ["//tests/binary-simple"],
+    deps = [":plugin-lib"],
+)
+
+haskell_test(
+    name = "binary-with-plugin",
+    srcs = ["Main.hs"],
+    plugins = select({
+        # XXX If profiling is enabled, ignore the plugin because of
+        # https://gitlab.haskell.org/ghc/ghc/issues/14335.
+        ":debug_build": [],
+        "//conditions:default": [":plugin"],
+    }),
+    visibility = ["//visibility:public"],
+    deps = [":base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-plugin/Main.hs b/third_party/bazel/rules_haskell/tests/binary-with-plugin/Main.hs
new file mode 100644
index 000000000000..2048dbdecd9a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-plugin/Main.hs
@@ -0,0 +1,3 @@
+module Main where
+
+main = putStrLn "hello world"
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-plugin/Plugin.hs b/third_party/bazel/rules_haskell/tests/binary-with-plugin/Plugin.hs
new file mode 100644
index 000000000000..3827bb799215
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-plugin/Plugin.hs
@@ -0,0 +1,15 @@
+module Plugin (plugin) where
+
+import Control.Monad (when)
+import GhcPlugins
+import System.Process (readProcess)
+
+plugin :: Plugin
+plugin = defaultPlugin { installCoreToDos = install }
+
+install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
+install [arg] todo = do
+  when ('$' `elem` arg) $
+    fail "Make variable not expanded."
+  _ <- liftIO $ readProcess arg [] ""
+  return todo
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-prebuilt/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-prebuilt/BUILD.bazel
new file mode 100644
index 000000000000..99a22ae9991f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-prebuilt/BUILD.bazel
@@ -0,0 +1,19 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "binary-with-prebuilt",
+    srcs = ["Main.hs"],
+    tags = ["requires_hackage"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+        "@hackage//:streaming",
+        "@hackage//:void",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-prebuilt/Main.hs b/third_party/bazel/rules_haskell/tests/binary-with-prebuilt/Main.hs
new file mode 100644
index 000000000000..aa3b1b40ee8a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-prebuilt/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import Data.List ()
+import Language.Haskell.TH ()
+
+main = return ()
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-sysdeps/BUILD.bazel b/third_party/bazel/rules_haskell/tests/binary-with-sysdeps/BUILD.bazel
new file mode 100644
index 000000000000..769ecf31a38c
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-sysdeps/BUILD.bazel
@@ -0,0 +1,17 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "binary-with-sysdeps",
+    srcs = ["Main.hs"],
+    tags = ["requires_zlib"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "@zlib",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/binary-with-sysdeps/Main.hs b/third_party/bazel/rules_haskell/tests/binary-with-sysdeps/Main.hs
new file mode 100644
index 000000000000..bf8da504168a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/binary-with-sysdeps/Main.hs
@@ -0,0 +1,8 @@
+module Main where
+
+import Foreign.Ptr
+import Foreign.C.Types
+
+foreign import ccall crc32 :: CLong -> Ptr () -> CInt -> IO ()
+
+main = crc32 0 nullPtr 0
diff --git a/third_party/bazel/rules_haskell/tests/c-compiles-still/BUILD.bazel b/third_party/bazel/rules_haskell/tests/c-compiles-still/BUILD.bazel
new file mode 100644
index 000000000000..314b895b6be4
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c-compiles-still/BUILD.bazel
@@ -0,0 +1,15 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "foo",
+    srcs = ["Foo.hs"],
+    deps = [
+        "//tests/data:ourclibrary",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/c-compiles-still/Foo.hs b/third_party/bazel/rules_haskell/tests/c-compiles-still/Foo.hs
new file mode 100644
index 000000000000..f325b2efcfbb
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c-compiles-still/Foo.hs
@@ -0,0 +1,4 @@
+module Foo (foo) where
+
+foo :: Int
+foo = 5
diff --git a/third_party/bazel/rules_haskell/tests/c-compiles/BUILD.bazel b/third_party/bazel/rules_haskell/tests/c-compiles/BUILD.bazel
new file mode 100644
index 000000000000..b5256bfb18d0
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c-compiles/BUILD.bazel
@@ -0,0 +1,26 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "hs-lib",
+    srcs = ["Lib.hs"],
+    deps = [
+        "//tests/data:ourclibrary",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_test(
+    name = "c-compiles",
+    srcs = ["Main.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":hs-lib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/c-compiles/Lib.hs b/third_party/bazel/rules_haskell/tests/c-compiles/Lib.hs
new file mode 100644
index 000000000000..6ebec6b460ce
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c-compiles/Lib.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Lib (ten) where
+
+import Foreign.C.Types (CInt(..))
+
+foreign import ccall "c_add_one"
+  c_add_one :: CInt -> CInt
+
+ten :: Int
+ten = fromIntegral (c_add_one 9)
diff --git a/third_party/bazel/rules_haskell/tests/c-compiles/Main.hs b/third_party/bazel/rules_haskell/tests/c-compiles/Main.hs
new file mode 100644
index 000000000000..5fc7b957c84b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c-compiles/Main.hs
@@ -0,0 +1,8 @@
+module Main (main) where
+
+import Control.Monad (unless)
+import Lib           (ten)
+
+main :: IO ()
+main = unless (ten == 10)
+    $ error $ "Incorrect lib value. Got " <> show ten
diff --git a/third_party/bazel/rules_haskell/tests/c-compiles/c-compiles.c b/third_party/bazel/rules_haskell/tests/c-compiles/c-compiles.c
new file mode 100644
index 000000000000..2d3a4d742c30
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c-compiles/c-compiles.c
@@ -0,0 +1 @@
+int add_five(int x) { return x + 5; }
diff --git a/third_party/bazel/rules_haskell/tests/c2hs/BUILD.bazel b/third_party/bazel/rules_haskell/tests/c2hs/BUILD.bazel
new file mode 100644
index 000000000000..a343c55f57fc
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c2hs/BUILD.bazel
@@ -0,0 +1,36 @@
+load("@io_tweag_rules_haskell//haskell:c2hs.bzl", "c2hs_library")
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(default_testonly = 1)
+
+c2hs_library(
+    name = "foo",
+    srcs = ["src/Foo/Foo.chs"],
+    src_strip_prefix = "src",
+    tags = [
+        "requires_c2hs",
+        "requires_zlib",
+    ],
+    deps = ["@zlib.dev//:zlib"],
+)
+
+c2hs_library(
+    name = "bar",
+    srcs = ["Bar.chs"],
+    tags = ["requires_c2hs"],
+    deps = [":foo"],
+)
+
+haskell_library(
+    name = "c2hs",
+    srcs = [
+        ":bar",
+        ":foo",
+        "@c2hs_repo//:baz",
+    ],
+    tags = ["requires_c2hs"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/c2hs/Bar.chs b/third_party/bazel/rules_haskell/tests/c2hs/Bar.chs
new file mode 100644
index 000000000000..1de32c1ccb3b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c2hs/Bar.chs
@@ -0,0 +1,6 @@
+module Bar (bar) where
+
+{#import Foo.Foo#}
+
+bar :: Int
+bar = 6
diff --git a/third_party/bazel/rules_haskell/tests/c2hs/repo/BUILD.bazel b/third_party/bazel/rules_haskell/tests/c2hs/repo/BUILD.bazel
new file mode 100644
index 000000000000..4f1d15c09a6f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c2hs/repo/BUILD.bazel
@@ -0,0 +1,10 @@
+load("@io_tweag_rules_haskell//haskell:c2hs.bzl", "c2hs_library")
+
+package(default_testonly = 1)
+
+c2hs_library(
+    name = "baz",
+    srcs = ["Baz.chs"],
+    visibility = ["//visibility:public"],
+    deps = ["@zlib.dev//:zlib"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/c2hs/repo/Baz.chs b/third_party/bazel/rules_haskell/tests/c2hs/repo/Baz.chs
new file mode 100644
index 000000000000..b6d74dfc4ea8
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c2hs/repo/Baz.chs
@@ -0,0 +1,6 @@
+module Baz (baz) where
+
+#include <zlib.h>
+
+baz :: Int
+baz = {# sizeof gz_header_s #}
diff --git a/third_party/bazel/rules_haskell/tests/c2hs/repo/WORKSPACE b/third_party/bazel/rules_haskell/tests/c2hs/repo/WORKSPACE
new file mode 100644
index 000000000000..e53539f0b46c
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c2hs/repo/WORKSPACE
@@ -0,0 +1 @@
+workspace(name = "c2hs_repo")
diff --git a/third_party/bazel/rules_haskell/tests/c2hs/src/Foo/Foo.chs b/third_party/bazel/rules_haskell/tests/c2hs/src/Foo/Foo.chs
new file mode 100644
index 000000000000..244f3b2b29ff
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/c2hs/src/Foo/Foo.chs
@@ -0,0 +1,6 @@
+module Foo.Foo (foo) where
+
+#include <zlib.h>
+
+foo :: Int
+foo = {# sizeof gz_header_s #}
diff --git a/third_party/bazel/rules_haskell/tests/cc_haskell_import/BUILD.bazel b/third_party/bazel/rules_haskell/tests/cc_haskell_import/BUILD.bazel
new file mode 100644
index 000000000000..c89631450059
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/cc_haskell_import/BUILD.bazel
@@ -0,0 +1,70 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "hs-lib-a",
+    srcs = ["LibA.hs"],
+    deps = [
+        "//tests/data:ourclibrary",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_library(
+    name = "hs-lib-b",
+    srcs = ["LibB.hs"],
+    deps = [
+        ":hs-lib-a",
+        "//tests/hackage:base",
+    ],
+)
+
+cc_binary(
+    name = "cc-bin",
+    srcs = [
+        "main.c",
+    ],
+    # TODO support linking with static haskell libraries.
+    linkstatic = False,
+    tags = ["requires_threaded_rts"],
+    visibility = ["//tests:__subpackages__"],
+    deps = [
+        ":hs-lib-b",
+        "@ghc//:threaded-rts",
+    ],
+)
+
+# We go one step further and use the Haskell library from above
+# to build a static .so which is then loaded with a Python script
+# and calls the Haskell function constructed from GHC C FFI.
+
+# shared library which python will dlopen
+cc_binary(
+    name = "hs-lib-b-wrapped.so",
+    linkshared = 1,
+    linkstatic = 0,
+    tags = ["requires_threaded_rts"],
+    visibility = ["//tests:__subpackages__"],
+    deps = [
+        ":hs-lib-b",
+        "@ghc//:threaded-rts",
+    ],
+)
+
+# just dlopens hb-lib-b-wrapped.so and prints it
+py_binary(
+    name = "python_add_one",
+    srcs = ["python_add_one.py"],
+    data = [
+        ":hs-lib-b-wrapped.so",
+    ],
+    default_python_version = "PY3",
+    srcs_version = "PY3ONLY",
+    tags = ["requires_threaded_rts"],
+    visibility = ["//tests:__subpackages__"],
+    deps = ["@bazel_tools//tools/python/runfiles"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/cc_haskell_import/LibA.hs b/third_party/bazel/rules_haskell/tests/cc_haskell_import/LibA.hs
new file mode 100644
index 000000000000..db8e442e5a5f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/cc_haskell_import/LibA.hs
@@ -0,0 +1,8 @@
+module LibA (add_one) where
+
+import Data.Int (Int32)
+
+foreign import ccall "c_add_one" c_add_one' :: Int32 -> Int32
+
+add_one :: Int32 -> Int32
+add_one x = c_add_one' x
diff --git a/third_party/bazel/rules_haskell/tests/cc_haskell_import/LibB.hs b/third_party/bazel/rules_haskell/tests/cc_haskell_import/LibB.hs
new file mode 100644
index 000000000000..5904463b51fc
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/cc_haskell_import/LibB.hs
@@ -0,0 +1,9 @@
+module LibB (add_one_hs) where
+
+import LibA (add_one)
+import Data.Int (Int32)
+
+foreign export ccall add_one_hs :: Int32 -> IO Int32
+
+add_one_hs :: Int32 -> IO Int32
+add_one_hs x = pure $! add_one x + 0
diff --git a/third_party/bazel/rules_haskell/tests/cc_haskell_import/cbits.c b/third_party/bazel/rules_haskell/tests/cc_haskell_import/cbits.c
new file mode 100644
index 000000000000..587d26ba28aa
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/cc_haskell_import/cbits.c
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+int32_t c_add_one(int32_t x) {
+  return 1 + x;
+}
diff --git a/third_party/bazel/rules_haskell/tests/cc_haskell_import/main.c b/third_party/bazel/rules_haskell/tests/cc_haskell_import/main.c
new file mode 100644
index 000000000000..661174e5df49
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/cc_haskell_import/main.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include "HsFFI.h"
+
+extern HsInt32 add_one_hs(HsInt32 a0);
+
+int main(int argc, char *argv[]) {
+  hs_init(&argc, &argv);
+  printf("Adding one to 5 through Haskell is %d\n", add_one_hs(5));
+  hs_exit();
+  return 0;
+}
diff --git a/third_party/bazel/rules_haskell/tests/cc_haskell_import/python_add_one.py b/third_party/bazel/rules_haskell/tests/cc_haskell_import/python_add_one.py
new file mode 100644
index 000000000000..b400102d1cc6
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/cc_haskell_import/python_add_one.py
@@ -0,0 +1,18 @@
+import os
+import ctypes
+from bazel_tools.tools.python.runfiles import runfiles
+import subprocess
+
+r = runfiles.Create()
+
+path = r.Rlocation('io_tweag_rules_haskell/tests/cc_haskell_import/hs-lib-b-wrapped.so')
+
+foreignlib = ctypes.cdll.LoadLibrary(path)
+
+# ATTN: If you remove this print *statement* hs_init will segfault!
+# If you use the python3 print *function*, it will segfault as well!
+# TODO: wtf?
+print foreignlib
+
+foreignlib.hs_init()
+assert(str(foreignlib.add_one_hs(1)) == "2")
diff --git a/third_party/bazel/rules_haskell/tests/cpp_macro_conflict/BUILD.bazel b/third_party/bazel/rules_haskell/tests/cpp_macro_conflict/BUILD.bazel
new file mode 100644
index 000000000000..af8848a6eea7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/cpp_macro_conflict/BUILD.bazel
@@ -0,0 +1,36 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+# empty library with package name "bytestring"
+haskell_library(
+    name = "bytestring",
+    srcs = ["src/BS.hs"],
+    deps = ["//tests/hackage:base"],
+)
+
+# This depends on two packages "bytestring"
+# There should be no CPP macro conflict
+haskell_test(
+    name = "macro_conflict",
+    srcs = ["Main.hs"],
+    compiler_flags = [
+        "-XCPP",
+        "-Werror",
+    ] + select({
+        # clang on darwin fails because of unused command line argument, it fails because of -Werror
+        "@bazel_tools//src/conditions:darwin": [
+            "-optP-Wno-unused-command-line-argument",
+        ],
+        "//conditions:default": [],
+    }),
+    deps = [
+        ":bytestring",
+        "//tests/hackage:base",
+        "//tests/hackage:bytestring",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/cpp_macro_conflict/Main.hs b/third_party/bazel/rules_haskell/tests/cpp_macro_conflict/Main.hs
new file mode 100644
index 000000000000..f5a27e6a7efb
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/cpp_macro_conflict/Main.hs
@@ -0,0 +1,4 @@
+import qualified Data.ByteString
+import BS
+
+main = putStrLn "hello"
diff --git a/third_party/bazel/rules_haskell/tests/cpp_macro_conflict/src/BS.hs b/third_party/bazel/rules_haskell/tests/cpp_macro_conflict/src/BS.hs
new file mode 100644
index 000000000000..437b7e5f72cd
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/cpp_macro_conflict/src/BS.hs
@@ -0,0 +1 @@
+module BS where
diff --git a/third_party/bazel/rules_haskell/tests/data/BUILD.bazel b/third_party/bazel/rules_haskell/tests/data/BUILD.bazel
new file mode 100644
index 000000000000..7083de23af40
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/data/BUILD.bazel
@@ -0,0 +1,15 @@
+# Generic data files and targets that are used by multiple tests
+
+cc_library(
+    name = "ourclibrary",
+    srcs = [":ourclibrary.c"],
+    linkstatic = False,
+    visibility = ["//visibility:public"],
+)
+
+cc_library(
+    name = "ourclibrary-static",
+    srcs = [":ourclibrary.c"],
+    linkstatic = True,
+    visibility = ["//visibility:public"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/data/ourclibrary.c b/third_party/bazel/rules_haskell/tests/data/ourclibrary.c
new file mode 100644
index 000000000000..587d26ba28aa
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/data/ourclibrary.c
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+int32_t c_add_one(int32_t x) {
+  return 1 + x;
+}
diff --git a/third_party/bazel/rules_haskell/tests/encoding/BUILD.bazel b/third_party/bazel/rules_haskell/tests/encoding/BUILD.bazel
new file mode 100644
index 000000000000..588d13f6d0b7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/encoding/BUILD.bazel
@@ -0,0 +1,21 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "encoding",
+    srcs = [
+        "Main.hs",
+        "TH.hs",
+    ],
+    extra_srcs = [
+        "unicode.txt",
+    ],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/encoding/Main.hs b/third_party/bazel/rules_haskell/tests/encoding/Main.hs
new file mode 100644
index 000000000000..beb5f2a29497
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/encoding/Main.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Main (main) where
+
+import TH
+
+main :: IO ()
+main = putStrLn $foo
diff --git a/third_party/bazel/rules_haskell/tests/encoding/TH.hs b/third_party/bazel/rules_haskell/tests/encoding/TH.hs
new file mode 100644
index 000000000000..8cb52b3f1139
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/encoding/TH.hs
@@ -0,0 +1,7 @@
+module TH (foo) where
+
+import Language.Haskell.TH
+import Language.Haskell.TH.Syntax (lift)
+
+foo :: Q Exp
+foo = runIO (readFile "tests/encoding/unicode.txt") >>= lift
diff --git a/third_party/bazel/rules_haskell/tests/encoding/unicode.txt b/third_party/bazel/rules_haskell/tests/encoding/unicode.txt
new file mode 100644
index 000000000000..74e5f2638d08
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/encoding/unicode.txt
@@ -0,0 +1 @@
+Некоторый текст на русском.
diff --git a/third_party/bazel/rules_haskell/tests/external-haskell-repository/BUILD.bazel b/third_party/bazel/rules_haskell/tests/external-haskell-repository/BUILD.bazel
new file mode 100644
index 000000000000..30ff35704050
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/external-haskell-repository/BUILD.bazel
@@ -0,0 +1,17 @@
+# Tests correct linking of haskell packages that were created
+# in a different bazel repository, e.g. with hazel.
+
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+haskell_test(
+    name = "external-haskell-repository",
+    srcs = ["Main.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "@haskell_package_repository_dummy//:library-with-cbits",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/external-haskell-repository/Main.hs b/third_party/bazel/rules_haskell/tests/external-haskell-repository/Main.hs
new file mode 100644
index 000000000000..97fcc158880a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/external-haskell-repository/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import AddOne
+import Control.Exception (assert)
+
+main = assert (addOne 41 == 42) $ return ()
diff --git a/third_party/bazel/rules_haskell/tests/external-haskell-repository/workspace_dummy.bzl b/third_party/bazel/rules_haskell/tests/external-haskell-repository/workspace_dummy.bzl
new file mode 100644
index 000000000000..1ccc0aadf1de
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/external-haskell-repository/workspace_dummy.bzl
@@ -0,0 +1,64 @@
+# This file constructs a dummy workspace to test
+# haskell binaries that are included from outside repositories
+# (because linking external repositories works differently).
+
+# Repo-ception, in the sense that we build a WORKSPACE
+# that references the workspaces already set up in the
+# `rules_haskell` WORKSPACE.
+def _haskell_package_repository_dummy_impl(rep_ctx):
+    rep_ctx.file(
+        "WORKSPACE",
+        executable = False,
+        content = """
+repository(name={name})
+
+register_toolchains(
+  "@io_tweag_rules_haskell//tests/:ghc"
+)
+""".format(name = rep_ctx.name),
+    )
+
+    # this mirrors tests/library-with-cbits
+
+    rep_ctx.file(
+        "BUILD",
+        executable = False,
+        content = """
+load(
+  "@io_tweag_rules_haskell//haskell:haskell.bzl",
+  "haskell_toolchain",
+  "haskell_library",
+)
+load(
+  "@io_tweag_rules_haskell//:constants.bzl",
+  "test_ghc_version",
+)
+
+haskell_library(
+  name = "library-with-cbits",
+  srcs = ["AddOne.hs"],
+  deps = [
+      "@io_tweag_rules_haskell//tests/data:ourclibrary",
+      "@io_tweag_rules_haskell//tests/hackage:base",
+  ],
+
+  linkstatic = False,
+  visibility = ["//visibility:public"],
+)
+""",
+    )
+
+    rep_ctx.file(
+        "AddOne.hs",
+        executable = False,
+        content = """
+module AddOne where
+
+foreign import ccall "c_add_one" addOne :: Int -> Int
+""",
+    )
+
+haskell_package_repository_dummy = repository_rule(
+    _haskell_package_repository_dummy_impl,
+    local = True,
+)
diff --git a/third_party/bazel/rules_haskell/tests/extra-source-files/BUILD.bazel b/third_party/bazel/rules_haskell/tests/extra-source-files/BUILD.bazel
new file mode 100644
index 000000000000..9552d7c9e974
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/extra-source-files/BUILD.bazel
@@ -0,0 +1,44 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "extra-source-files",
+    srcs = [
+        "Foo.hs",
+        "FooTH.hs",
+    ],
+    # Test that the linker can also see the extra_srcs.
+    compiler_flags = ["-optl-Wl,@tests/extra-source-files/ld-options.txt"],
+    extra_srcs = [
+        "file.txt",
+        "ld-options.txt",
+    ],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+    ],
+)
+
+haskell_test(
+    name = "extra-source-files-bin",
+    srcs = [
+        "Foo.hs",
+        "FooTH.hs",
+        "Main.hs",
+    ],
+    # Test that the linker can also see the extra_srcs.
+    compiler_flags = ["-optl-Wl,@tests/extra-source-files/ld-options.txt"],
+    extra_srcs = [
+        "file.txt",
+        "ld-options.txt",
+    ],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/extra-source-files/Foo.hs b/third_party/bazel/rules_haskell/tests/extra-source-files/Foo.hs
new file mode 100644
index 000000000000..b3f0c04492db
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/extra-source-files/Foo.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Foo (foo) where
+
+import FooTH (embedFile)
+
+foo :: String
+foo = $(embedFile "tests/extra-source-files/file.txt") ++ "!"
diff --git a/third_party/bazel/rules_haskell/tests/extra-source-files/FooTH.hs b/third_party/bazel/rules_haskell/tests/extra-source-files/FooTH.hs
new file mode 100644
index 000000000000..24d663108355
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/extra-source-files/FooTH.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module FooTH (embedFile) where
+
+import Language.Haskell.TH
+import Language.Haskell.TH.Syntax
+
+embedFile :: FilePath -> Q Exp
+embedFile path = do
+  str <- runIO (readFile path)
+  addDependentFile path
+  [| str |]
diff --git a/third_party/bazel/rules_haskell/tests/extra-source-files/Main.hs b/third_party/bazel/rules_haskell/tests/extra-source-files/Main.hs
new file mode 100644
index 000000000000..13e4bd765b6a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/extra-source-files/Main.hs
@@ -0,0 +1,4 @@
+import Foo (foo)
+
+main :: IO ()
+main = putStrLn foo
diff --git a/third_party/bazel/rules_haskell/tests/extra-source-files/file.txt b/third_party/bazel/rules_haskell/tests/extra-source-files/file.txt
new file mode 100644
index 000000000000..5bc133a4d007
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/extra-source-files/file.txt
@@ -0,0 +1 @@
+And here we go
diff --git a/third_party/bazel/rules_haskell/tests/extra-source-files/ld-options.txt b/third_party/bazel/rules_haskell/tests/extra-source-files/ld-options.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/extra-source-files/ld-options.txt
diff --git a/third_party/bazel/rules_haskell/tests/failures/transitive-deps/BUILD.bazel b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/BUILD.bazel
new file mode 100644
index 000000000000..c2efbd924a32
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/BUILD.bazel
@@ -0,0 +1,66 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "lib-a",
+    srcs = ["LibA.hs"],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_library(
+    name = "lib-b",
+    srcs = ["LibB.hs"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":lib-a",
+        "//tests/hackage:base",
+    ],
+)
+
+# Targets that must FAIL. These are tagged as manual so that
+#
+# $ bazel build //...
+#
+# does not fail.
+
+haskell_library(
+    # Should fail because it doesn't specify "base" explicitly.
+    name = "lib-cFailure",
+    srcs = ["LibC.hs"],
+    tags = ["manual"],
+    deps = [":lib-b"],
+)
+
+haskell_library(
+    name = "lib-c",
+    srcs = ["LibC.hs"],
+    deps = [
+        ":lib-b",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_library(
+    # Should fail because it doesn't specify "lib-a" explicitly.
+    name = "lib-dFailure",
+    srcs = ["LibD.hs"],
+    tags = ["manual"],
+    deps = [
+        ":lib-b",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_library(
+    name = "lib-d",
+    srcs = ["LibD.hs"],
+    deps = [
+        ":lib-a",
+        ":lib-b",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibA.hs b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibA.hs
new file mode 100644
index 000000000000..23e9486489fe
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibA.hs
@@ -0,0 +1,4 @@
+module LibA (thingA) where
+
+thingA :: Int
+thingA = 5
diff --git a/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibB.hs b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibB.hs
new file mode 100644
index 000000000000..9ab883093bd8
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibB.hs
@@ -0,0 +1,6 @@
+module LibB (thingB) where
+
+import LibA (thingA)
+
+thingB :: Int
+thingB = thingA + 1
diff --git a/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibC.hs b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibC.hs
new file mode 100644
index 000000000000..70514f13168b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibC.hs
@@ -0,0 +1,6 @@
+module LibC (thingC) where
+
+import LibB (thingB)
+
+thingC :: Int
+thingC = thingB * 2
diff --git a/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibD.hs b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibD.hs
new file mode 100644
index 000000000000..326d224a2a15
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/failures/transitive-deps/LibD.hs
@@ -0,0 +1,7 @@
+module LibD (thingD) where
+
+import LibA (thingA)
+import LibB (thingB)
+
+thingD :: Int
+thingD = thingA + thingB
diff --git a/third_party/bazel/rules_haskell/tests/generated-modules/BUILD.bazel b/third_party/bazel/rules_haskell/tests/generated-modules/BUILD.bazel
new file mode 100644
index 000000000000..098c7148a960
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/generated-modules/BUILD.bazel
@@ -0,0 +1,56 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(
+    default_testonly = 1,
+    default_visibility = ["//visibility:public"],
+)
+
+genrule(
+    name = "generate-genmodule",
+    outs = ["src/GenModule.hs"],
+    cmd = "printf 'module GenModule where\na = 1 :: Int' > $@",
+)
+
+haskell_library(
+    name = "GenModule",
+    srcs = [":generate-genmodule"],
+    src_strip_prefix = "src",
+    deps = ["//tests/hackage:base"],
+)
+
+genrule(
+    name = "generate-binmodule",
+    outs = ["src/BinModule.hs"],
+    cmd = "printf 'module BinModule where\nb = 2 :: Int' > $@",
+    output_to_bindir = 1,
+)
+
+haskell_library(
+    name = "BinModule",
+    srcs = [":generate-binmodule"],
+    src_strip_prefix = "src",
+    deps = ["//tests/hackage:base"],
+)
+
+genrule(
+    name = "generate-main",
+    outs = ["src/Main.hs"],
+    cmd = "printf 'module Main where\nimport GenModule\nimport BinModule\n" +
+          "main = print (a+b) :: IO ()' > $@",
+)
+
+haskell_test(
+    name = "generated-modules",
+    size = "small",
+    srcs = [":generate-main"],
+    src_strip_prefix = "src",
+    deps = [
+        ":BinModule",
+        ":GenModule",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/ghc.nix b/third_party/bazel/rules_haskell/tests/ghc.nix
new file mode 100644
index 000000000000..cde1699918f2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/ghc.nix
@@ -0,0 +1,41 @@
+{ pkgs ? import ../nixpkgs {}
+# Whether we want to wrap the packages using <bazel_haskell_wrapper>.
+# When this is called from inside bazel, we need to wrap the haskell package
+# set using <bazel_haskell_wrapper>. Otherwise we don't need (and don't want)
+# to.
+, wrapPackages ? (builtins.tryEval <bazel_haskell_wrapper>).success
+}:
+
+with pkgs;
+
+let haskellPackages = pkgs.haskell.packages.ghc864.override {
+      overrides = with pkgs.haskell.lib; self: super: rec {
+        libc = import ./haddock/libC.nix self pkgs;
+      };
+    };
+    genBazelBuild =
+      if wrapPackages
+      then callPackage <bazel_haskell_wrapper> {}
+      else (x: x);
+
+in
+  {
+  ghc = haskellPackages.ghcWithPackages (p: with p; [
+
+    # haskell_proto_library inputs
+    bytestring
+    containers
+    data-default-class
+    lens-family
+    lens-labels
+    proto-lens
+    text
+
+  # test inputs
+  libc
+
+  # for test runner
+  hspec
+  ]);
+  haskellPackages = genBazelBuild haskellPackages;
+}
diff --git a/third_party/bazel/rules_haskell/tests/ghc/BUILD.bazel b/third_party/bazel/rules_haskell/tests/ghc/BUILD.bazel
new file mode 100644
index 000000000000..345ce4badf91
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/ghc/BUILD.bazel
@@ -0,0 +1,3 @@
+load("//tests/ghc:ghc.bzl", "ghc_help")
+
+ghc_help(name = "ghc_help")
diff --git a/third_party/bazel/rules_haskell/tests/ghc/ghc.bzl b/third_party/bazel/rules_haskell/tests/ghc/ghc.bzl
new file mode 100644
index 000000000000..443f04e7be63
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/ghc/ghc.bzl
@@ -0,0 +1,19 @@
+"""Runs ghc --help"""
+
+hs_toolchain = "@io_tweag_rules_haskell//haskell:toolchain"
+
+def _impl(ctx):
+    output = ctx.outputs.out
+    ghc = ctx.toolchains[hs_toolchain].tools.ghc
+    ctx.actions.run_shell(
+        inputs = [ghc],
+        outputs = [output],
+        progress_message = "Printing ghc help message",
+        command = "%s --help > %s" % (ghc.path, output.path),
+    )
+
+ghc_help = rule(
+    implementation = _impl,
+    outputs = {"out": "out_file"},
+    toolchains = [hs_toolchain],
+)
diff --git a/third_party/bazel/rules_haskell/tests/hackage/BUILD.bazel b/third_party/bazel/rules_haskell/tests/hackage/BUILD.bazel
new file mode 100644
index 000000000000..63fb6f941b4b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hackage/BUILD.bazel
@@ -0,0 +1,34 @@
+"""
+    Fetches GHC boot packages from GHC directly rather than from Nixpkgs
+    for better bindist support.
+"""
+
+package(default_visibility = [
+    "//tests:__subpackages__",
+    "@haskell_package_repository_dummy//:__subpackages__",
+])
+
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_toolchain_library",
+)
+
+[
+    haskell_toolchain_library(name = name)
+    for name in [
+        "array",
+        "base",
+        "binary",
+        "bytestring",
+        "containers",
+        "deepseq",
+        "directory",
+        "filepath",
+        "mtl",
+        "template-haskell",
+        "transformers",
+        "ghc-prim",
+        "process",
+        "text",
+    ]
+]
diff --git a/third_party/bazel/rules_haskell/tests/haddock/BUILD.bazel b/third_party/bazel/rules_haskell/tests/haddock/BUILD.bazel
new file mode 100644
index 000000000000..6955285c87d2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haddock/BUILD.bazel
@@ -0,0 +1,73 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_doc",
+    "haskell_library",
+    "haskell_toolchain_library",
+)
+
+package(
+    default_testonly = 1,
+    default_visibility = ["//visibility:public"],
+)
+
+haskell_library(
+    name = "haddock-lib-deep",
+    srcs = ["Deep.hsc"],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_library(
+    name = "haddock-lib-a",
+    srcs = [
+        "LibA.hs",
+        "LibA/A.hs",
+        "header.h",
+    ],
+    compiler_flags = ["-I."],
+    deps = [
+        ":haddock-lib-deep",
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+    ],
+)
+
+haskell_toolchain_library(
+    name = "haddock-lib-c",
+    package = "libc",
+)
+
+haskell_library(
+    name = "haddock-lib-b",
+    srcs = [
+        "LibB.hs",
+        "TH.hs",
+    ],
+    extra_srcs = [
+        "unicode.txt",
+    ],
+    tags = [
+        "requires_hackage",
+        "requires_zlib",
+    ],
+    deps = [
+        ":haddock-lib-a",
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+        "@hackage//:libc",
+        "@zlib",
+    ],
+)
+
+haskell_doc(
+    name = "haddock",
+    index_transitive_deps = False,
+    tags = ["requires_hackage"],
+    deps = [":haddock-lib-b"],
+)
+
+haskell_doc(
+    name = "haddock-transitive",
+    index_transitive_deps = True,
+    tags = ["requires_hackage"],
+    deps = [":haddock-lib-b"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/haddock/Deep.hsc b/third_party/bazel/rules_haskell/tests/haddock/Deep.hsc
new file mode 100644
index 000000000000..6fb0482a1572
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haddock/Deep.hsc
@@ -0,0 +1,11 @@
+-- | "Deep" doc.
+module Deep (deep_lib) where
+
+-- | 'deep_lib' doc.
+
+#if __GLASGOW_HASKELL__ >= 700
+#ifndef _INTERNAL_HSC_DO_NOT_DEFINE_ME
+deep_lib :: Int
+deep_lib = 100
+#endif
+#endif
diff --git a/third_party/bazel/rules_haskell/tests/haddock/LibA.hs b/third_party/bazel/rules_haskell/tests/haddock/LibA.hs
new file mode 100644
index 000000000000..252c90ccc6d9
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haddock/LibA.hs
@@ -0,0 +1,19 @@
+-- | "LibA" header
+{-# LANGUAGE CPP             #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+#include "header.h"
+
+module LibA where
+
+import LibA.A (a)
+import Deep (deep_lib)
+
+-- | 'A' declaration.
+data A =
+  -- | 'A' constructor.
+  A
+
+-- | Doc for 'f' using 'a' and 'deep_lib'.
+f :: Int
+f = const $a deep_lib + MAGIC_NUMBER
diff --git a/third_party/bazel/rules_haskell/tests/haddock/LibA/A.hs b/third_party/bazel/rules_haskell/tests/haddock/LibA/A.hs
new file mode 100644
index 000000000000..3be8dd5aadd3
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haddock/LibA/A.hs
@@ -0,0 +1,10 @@
+-- | "LibA.A" header
+{-# LANGUAGE TemplateHaskell #-}
+
+module LibA.A where
+
+import Language.Haskell.TH
+
+-- | 'a' doc
+a :: Q Exp
+a = [| 5 |]
diff --git a/third_party/bazel/rules_haskell/tests/haddock/LibB.hs b/third_party/bazel/rules_haskell/tests/haddock/LibB.hs
new file mode 100644
index 000000000000..b4df1ffcb9a2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haddock/LibB.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE TemplateHaskell #-}
+-- | "LibB" doc.
+
+module LibB where
+
+import LibA.A (a)
+import LibA (f)
+import LibC (mytype, LibCType)
+import TH (foo)
+
+-- | Doc for 'x' using 'f' and 'a' and 'Int'.
+x :: Int
+x = const f a
+
+-- | This uses a type from an undocumented package
+y :: LibCType
+y = mytype
+
+-- | A thing generated with TH.
+z :: String
+z = $foo
diff --git a/third_party/bazel/rules_haskell/tests/haddock/TH.hs b/third_party/bazel/rules_haskell/tests/haddock/TH.hs
new file mode 100644
index 000000000000..6e791b09154e
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haddock/TH.hs
@@ -0,0 +1,7 @@
+module TH (foo) where
+
+import Language.Haskell.TH
+import Language.Haskell.TH.Syntax (lift)
+
+foo :: Q Exp
+foo = runIO (readFile "tests/haddock/unicode.txt") >>= lift
diff --git a/third_party/bazel/rules_haskell/tests/haddock/header.h b/third_party/bazel/rules_haskell/tests/haddock/header.h
new file mode 100644
index 000000000000..ec5eddd2766e
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haddock/header.h
@@ -0,0 +1 @@
+#define MAGIC_NUMBER 100
diff --git a/third_party/bazel/rules_haskell/tests/haddock/libC.nix b/third_party/bazel/rules_haskell/tests/haddock/libC.nix
new file mode 100644
index 000000000000..d345178838f1
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haddock/libC.nix
@@ -0,0 +1,47 @@
+# A trivial `haskellPackages` library that has haddock generation disabled
+self: pkgs:
+let
+  # pkgs = import ../../nixpkgs.nix {};
+
+  libC = pkgs.writeText "LibC.hs" ''
+    {-# language NoImplicitPrelude #-}
+    module LibC where
+
+    data LibCType = LibCType
+
+    -- | myfunction
+    mytype :: LibCType
+    mytype = LibCType
+  '';
+
+  cabal = pkgs.writeText "libc.cabal" ''
+    name: libc
+    version: 0.1.0.0
+    build-type: Simple
+    cabal-version: >=1.10
+
+    library
+      default-language: Haskell2010
+      exposed-modules: LibC
+   '';
+
+   src = pkgs.runCommand "libc-src" {} ''
+     mkdir $out
+     cp ${libC} $out/LibC.hs
+     cp ${cabal} $out/libc.cabal
+   '';
+
+in
+  # This call means the `.haddock` file is not generated,
+  # even though the ghc package still references the location
+  # where it would ordinarily be.
+  pkgs.haskell.lib.dontHaddock
+
+    (self.callPackage
+      ({ mkDerivation }: mkDerivation {
+        pname = "libc";
+        version = "0.1.0.0";
+        src = src;
+        license = pkgs.lib.licenses.mit;
+        isExecutable = false;
+      }) {})
diff --git a/third_party/bazel/rules_haskell/tests/haddock/unicode.txt b/third_party/bazel/rules_haskell/tests/haddock/unicode.txt
new file mode 100644
index 000000000000..74e5f2638d08
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haddock/unicode.txt
@@ -0,0 +1 @@
+Некоторый текст на русском.
diff --git a/third_party/bazel/rules_haskell/tests/haskell_doctest/BUILD.bazel b/third_party/bazel/rules_haskell/tests/haskell_doctest/BUILD.bazel
new file mode 100644
index 000000000000..c1d4e527aefc
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_doctest/BUILD.bazel
@@ -0,0 +1,73 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_doctest",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "lib-a",
+    srcs = ["Foo.hs"],
+    tags = ["requires_zlib"],
+    deps = [
+        "//tests/data:ourclibrary",
+        "//tests/hackage:base",
+        "@zlib.dev//:zlib",
+    ],
+)
+
+haskell_library(
+    name = "lib-b",
+    srcs = [
+        "Bar.hs",
+        "Baz.hs",
+        "Quux.hsc",
+    ],
+    tags = ["requires_zlib"],
+    deps = [
+        ":lib-a",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_doctest(
+    name = "doctest-lib-all-fail",
+    tags = ["manual"],  # must FAIL
+    visibility = ["//visibility:public"],
+    deps = [":lib-b"],
+)
+
+haskell_doctest(
+    name = "doctest-lib-all-success",
+    doctest_flags = ["-DMAGIC_DOCTEST_THING"],
+    tags = ["requires_doctest"],
+    visibility = ["//visibility:public"],
+    deps = [":lib-b"],
+)
+
+haskell_doctest(
+    name = "doctest-lib",
+    modules = ["Bar"],  # exclude Baz and succeed
+    tags = ["requires_doctest"],
+    visibility = ["//visibility:public"],
+    deps = [":lib-b"],
+)
+
+haskell_test(
+    name = "bin",
+    srcs = ["Main.hs"],
+    tags = ["requires_zlib"],
+    deps = [
+        ":lib-a",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_doctest(
+    name = "doctest-bin",
+    tags = ["requires_doctest"],
+    visibility = ["//visibility:public"],
+    deps = [":bin"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/haskell_doctest/Bar.hs b/third_party/bazel/rules_haskell/tests/haskell_doctest/Bar.hs
new file mode 100644
index 000000000000..cd82351855c3
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_doctest/Bar.hs
@@ -0,0 +1,13 @@
+module Bar (bar) where
+
+import Foo (foo)
+import Numeric
+
+-- |
+-- >>> bar
+-- 9
+-- >>> showInt bar "" ++ "!"
+-- "9!"
+
+bar :: Int
+bar = 4 + foo
diff --git a/third_party/bazel/rules_haskell/tests/haskell_doctest/Baz.hs b/third_party/bazel/rules_haskell/tests/haskell_doctest/Baz.hs
new file mode 100644
index 000000000000..d576235bf488
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_doctest/Baz.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE CPP #-}
+
+module Baz (baz) where
+
+-- |
+-- >>> baz
+-- 101
+
+baz :: Int
+#ifndef MAGIC_DOCTEST_THING
+baz = 100
+#else
+baz = 101
+#endif
diff --git a/third_party/bazel/rules_haskell/tests/haskell_doctest/Foo.hs b/third_party/bazel/rules_haskell/tests/haskell_doctest/Foo.hs
new file mode 100644
index 000000000000..d2c97b77aeb3
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_doctest/Foo.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+module Foo (foo) where
+
+import Foreign.C.Types (CInt(..))
+
+foreign import ccall "c_add_one"
+  c_add_one :: CInt -> CInt
+
+-- |
+-- >>> foo
+-- 5
+foo :: Int
+foo = fromIntegral (c_add_one 4)
diff --git a/third_party/bazel/rules_haskell/tests/haskell_doctest/Main.hs b/third_party/bazel/rules_haskell/tests/haskell_doctest/Main.hs
new file mode 100644
index 000000000000..1bc6f8abc905
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_doctest/Main.hs
@@ -0,0 +1,6 @@
+module Main (main) where
+
+import Foo (foo)
+
+main :: IO ()
+main = print foo
diff --git a/third_party/bazel/rules_haskell/tests/haskell_doctest/Quux.hsc b/third_party/bazel/rules_haskell/tests/haskell_doctest/Quux.hsc
new file mode 100644
index 000000000000..3d437274c9aa
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_doctest/Quux.hsc
@@ -0,0 +1,11 @@
+-- | This module is in a .hsc file, this way we test that improt directories
+-- are passed correctly to the doctest executable.
+
+module Quux (quux) where
+
+-- |
+-- >>> quux
+-- 68
+
+quux :: Int
+quux = 68
diff --git a/third_party/bazel/rules_haskell/tests/haskell_lint/BUILD.bazel b/third_party/bazel/rules_haskell/tests/haskell_lint/BUILD.bazel
new file mode 100644
index 000000000000..702dfdfe07bc
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_lint/BUILD.bazel
@@ -0,0 +1,47 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_lint",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "lib-a",
+    srcs = ["Foo.hs"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_library(
+    name = "lib-b",
+    srcs = ["Bar.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":lib-a",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_lint(
+    name = "lint-lib-b",
+    visibility = ["//visibility:public"],
+    deps = [":lib-b"],
+)
+
+haskell_test(
+    name = "bin",
+    srcs = ["Main.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":lib-a",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_lint(
+    name = "lint-bin",
+    visibility = ["//visibility:public"],
+    deps = [":bin"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/haskell_lint/Bar.hs b/third_party/bazel/rules_haskell/tests/haskell_lint/Bar.hs
new file mode 100644
index 000000000000..18e9583e5ad6
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_lint/Bar.hs
@@ -0,0 +1,6 @@
+module Bar (bar) where
+
+import Foo (foo)
+
+bar :: Int
+bar = 4 + foo :: Int
diff --git a/third_party/bazel/rules_haskell/tests/haskell_lint/Foo.hs b/third_party/bazel/rules_haskell/tests/haskell_lint/Foo.hs
new file mode 100644
index 000000000000..119192e6b12b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_lint/Foo.hs
@@ -0,0 +1,4 @@
+module Foo (foo) where
+
+foo :: Int
+foo = 5 :: Int
diff --git a/third_party/bazel/rules_haskell/tests/haskell_lint/Main.hs b/third_party/bazel/rules_haskell/tests/haskell_lint/Main.hs
new file mode 100644
index 000000000000..1bc6f8abc905
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_lint/Main.hs
@@ -0,0 +1,6 @@
+module Main (main) where
+
+import Foo (foo)
+
+main :: IO ()
+main = print foo
diff --git a/third_party/bazel/rules_haskell/tests/haskell_proto_library/BUILD.bazel b/third_party/bazel/rules_haskell/tests/haskell_proto_library/BUILD.bazel
new file mode 100644
index 000000000000..f5f8762b3aff
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_proto_library/BUILD.bazel
@@ -0,0 +1,78 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_doc",
+    "haskell_library",
+    "haskell_proto_library",
+)
+
+package(default_testonly = 1)
+
+proto_library(
+    name = "zip_code_proto",
+    srcs = ["zip_code.proto"],
+)
+
+proto_library(
+    name = "address_proto",
+    srcs = ["address.proto"],
+    deps = [":zip_code_proto"],
+)
+
+proto_library(
+    name = "person_proto",
+    srcs = ["person.proto"],
+    deps = [
+        ":address_proto",
+        "@com_google_protobuf//:timestamp_proto",
+    ],
+)
+
+proto_library(
+    name = "stripped_zip_code_proto",
+    srcs = ["stripped_zip_code.proto"],
+    strip_import_prefix = "/tests/haskell_proto_library/",
+)
+
+proto_library(
+    name = "stripped_address_proto",
+    srcs = ["stripped_address.proto"],
+    strip_import_prefix = "/tests/haskell_proto_library/",
+    deps = [":stripped_zip_code_proto"],
+)
+
+haskell_proto_library(
+    name = "address_haskell_proto",
+    tags = ["requires_hackage"],
+    deps = [":address_proto"],
+)
+
+haskell_proto_library(
+    name = "stripped_address_haskell_proto",
+    tags = ["requires_hackage"],
+    deps = [":stripped_address_proto"],
+)
+
+haskell_proto_library(
+    name = "person_haskell_proto",
+    tags = ["requires_hackage"],
+    deps = [":person_proto"],
+)
+
+haskell_library(
+    name = "hs-lib",
+    srcs = ["Bar.hs"],
+    tags = ["requires_hackage"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":address_haskell_proto",
+        ":person_haskell_proto",
+        ":stripped_address_haskell_proto",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_doc(
+    name = "docs",
+    tags = ["requires_hackage"],
+    deps = [":hs-lib"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/haskell_proto_library/Bar.hs b/third_party/bazel/rules_haskell/tests/haskell_proto_library/Bar.hs
new file mode 100644
index 000000000000..fa95bf088c91
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_proto_library/Bar.hs
@@ -0,0 +1,8 @@
+module Bar (bar) where
+
+import Proto.StrippedAddress
+import Proto.Tests.HaskellProtoLibrary.Person
+import Proto.Tests.HaskellProtoLibrary.Person_Fields
+
+bar :: Int
+bar = 5
diff --git a/third_party/bazel/rules_haskell/tests/haskell_proto_library/address.proto b/third_party/bazel/rules_haskell/tests/haskell_proto_library/address.proto
new file mode 100644
index 000000000000..d81642d8bcd4
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_proto_library/address.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package demo; // Required to generate valid code.
+
+// Always import protos with a full path relative to the WORKSPACE file.
+import "tests/haskell_proto_library/zip_code.proto";
+
+message Address {
+  string city = 1;
+  ZipCode zip_code = 2;
+}
diff --git a/third_party/bazel/rules_haskell/tests/haskell_proto_library/person.proto b/third_party/bazel/rules_haskell/tests/haskell_proto_library/person.proto
new file mode 100644
index 000000000000..95bf3524f819
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_proto_library/person.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+
+package demo; // Required to generate valid code.
+
+// Always import protos with a full path relative to the WORKSPACE file.
+import "tests/haskell_proto_library/address.proto";
+
+import "google/protobuf/timestamp.proto";
+
+message Person {
+  string name = 1;
+  int32 id = 2;
+  string email = 3;
+  Address address = 4;
+  google.protobuf.Timestamp timestamp = 5;
+}
diff --git a/third_party/bazel/rules_haskell/tests/haskell_proto_library/stripped_address.proto b/third_party/bazel/rules_haskell/tests/haskell_proto_library/stripped_address.proto
new file mode 100644
index 000000000000..5b5ce47be83a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_proto_library/stripped_address.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package demo; // Required to generate valid code.
+
+import "stripped_zip_code.proto";
+
+message StrippedAddress {
+  string city = 1;
+  StrippedZipCode zip_code = 2;
+}
diff --git a/third_party/bazel/rules_haskell/tests/haskell_proto_library/stripped_zip_code.proto b/third_party/bazel/rules_haskell/tests/haskell_proto_library/stripped_zip_code.proto
new file mode 100644
index 000000000000..690d7c9d99cc
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_proto_library/stripped_zip_code.proto
@@ -0,0 +1,7 @@
+syntax = "proto3";
+
+package demo; // Required to generate valid code.
+
+message StrippedZipCode {
+  string code = 1;
+}
diff --git a/third_party/bazel/rules_haskell/tests/haskell_proto_library/zip_code.proto b/third_party/bazel/rules_haskell/tests/haskell_proto_library/zip_code.proto
new file mode 100644
index 000000000000..e72578cf2178
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_proto_library/zip_code.proto
@@ -0,0 +1,7 @@
+syntax = "proto3";
+
+package demo; // Required to generate valid code.
+
+message ZipCode {
+  string code = 1;
+}
diff --git a/third_party/bazel/rules_haskell/tests/haskell_test/BUILD.bazel b/third_party/bazel/rules_haskell/tests/haskell_test/BUILD.bazel
new file mode 100644
index 000000000000..eb00b2c31881
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_test/BUILD.bazel
@@ -0,0 +1,29 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "mylib",
+    srcs = ["Lib.hs"],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_test(
+    name = "haskell_test",
+    # Use some parameters that only test rules have.
+    size = "small",
+    timeout = "short",
+    srcs = ["Test.hs"],
+    flaky = False,
+    main_function = "Test.test",
+    visibility = ["//visibility:public"],
+    deps = [
+        ":mylib",
+        "//tests/data:ourclibrary",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/haskell_test/Lib.hs b/third_party/bazel/rules_haskell/tests/haskell_test/Lib.hs
new file mode 100644
index 000000000000..3553d6b6f493
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_test/Lib.hs
@@ -0,0 +1,4 @@
+module Lib where
+
+foo :: Int
+foo = 42
diff --git a/third_party/bazel/rules_haskell/tests/haskell_test/Test.hs b/third_party/bazel/rules_haskell/tests/haskell_test/Test.hs
new file mode 100644
index 000000000000..5ba65f82c94b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_test/Test.hs
@@ -0,0 +1,4 @@
+module Test (test) where
+
+test :: IO ()
+test = putStrLn "haskell_test fired"
diff --git a/third_party/bazel/rules_haskell/tests/haskell_toolchain_library/BUILD.bazel b/third_party/bazel/rules_haskell/tests/haskell_toolchain_library/BUILD.bazel
new file mode 100644
index 000000000000..6c36ce4bdbf1
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_toolchain_library/BUILD.bazel
@@ -0,0 +1,26 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "Lib",
+    srcs = ["Lib.hs"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:bytestring",
+    ],
+)
+
+haskell_test(
+    name = "binary",
+    srcs = ["Bin.hs"],
+    deps = [
+        ":Lib",
+        "//tests/hackage:base",
+        "//tests/hackage:text",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/haskell_toolchain_library/Bin.hs b/third_party/bazel/rules_haskell/tests/haskell_toolchain_library/Bin.hs
new file mode 100644
index 000000000000..a0e7117d2e22
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_toolchain_library/Bin.hs
@@ -0,0 +1,8 @@
+module Main (main) where
+
+import qualified Data.Text.IO as T
+import qualified Data.Text.Encoding as E
+
+import Lib (message)
+
+main = T.putStrLn $ E.decodeUtf8 message
diff --git a/third_party/bazel/rules_haskell/tests/haskell_toolchain_library/Lib.hs b/third_party/bazel/rules_haskell/tests/haskell_toolchain_library/Lib.hs
new file mode 100644
index 000000000000..66a2161a19c4
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/haskell_toolchain_library/Lib.hs
@@ -0,0 +1,5 @@
+module Lib (message) where
+
+import qualified Data.ByteString.Char8 as B
+
+message = B.pack "hello, world"
diff --git a/third_party/bazel/rules_haskell/tests/hidden-modules/BUILD.bazel b/third_party/bazel/rules_haskell/tests/hidden-modules/BUILD.bazel
new file mode 100644
index 000000000000..5d79e56085a2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hidden-modules/BUILD.bazel
@@ -0,0 +1,35 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(
+    default_testonly = 1,
+    default_visibility = ["//visibility:public"],
+)
+
+haskell_library(
+    name = "lib-a",
+    srcs = glob(["lib-a/*.hs"]),
+    hidden_modules = ["Foo"],
+    src_strip_prefix = "lib-a",
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_library(
+    name = "lib-b",
+    srcs = glob(["lib-b/*.hs"]),
+    src_strip_prefix = "lib-b",
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_library(
+    name = "lib-c",
+    srcs = glob(["lib-c/*.hs"]),
+    src_strip_prefix = "lib-c",
+    deps = [
+        ":lib-a",
+        ":lib-b",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/hidden-modules/lib-a/Bar.hs b/third_party/bazel/rules_haskell/tests/hidden-modules/lib-a/Bar.hs
new file mode 100644
index 000000000000..5bc9c68895bf
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hidden-modules/lib-a/Bar.hs
@@ -0,0 +1,6 @@
+module Bar (bar) where
+
+import Foo (foo)
+
+bar :: Int
+bar = foo * 2
diff --git a/third_party/bazel/rules_haskell/tests/hidden-modules/lib-a/Foo.hs b/third_party/bazel/rules_haskell/tests/hidden-modules/lib-a/Foo.hs
new file mode 100644
index 000000000000..664be00d5487
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hidden-modules/lib-a/Foo.hs
@@ -0,0 +1,4 @@
+module Foo (foo) where
+
+foo :: Int
+foo = 15
diff --git a/third_party/bazel/rules_haskell/tests/hidden-modules/lib-b/Foo.hs b/third_party/bazel/rules_haskell/tests/hidden-modules/lib-b/Foo.hs
new file mode 100644
index 000000000000..6fdd42f9a8cd
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hidden-modules/lib-b/Foo.hs
@@ -0,0 +1,4 @@
+module Foo (foo) where
+
+foo :: Int
+foo = 16
diff --git a/third_party/bazel/rules_haskell/tests/hidden-modules/lib-c/Baz.hs b/third_party/bazel/rules_haskell/tests/hidden-modules/lib-c/Baz.hs
new file mode 100644
index 000000000000..055b2e80ad79
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hidden-modules/lib-c/Baz.hs
@@ -0,0 +1,7 @@
+module Baz (bar) where
+
+import Foo (foo)
+import Bar (bar)
+
+baz :: Int
+baz = foo + bar
diff --git a/third_party/bazel/rules_haskell/tests/hs-boot/A.hs-boot.in b/third_party/bazel/rules_haskell/tests/hs-boot/A.hs-boot.in
new file mode 100644
index 000000000000..02d37ced950f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hs-boot/A.hs-boot.in
@@ -0,0 +1,2 @@
+module A where
+  newtype TA = MkTA Int
diff --git a/third_party/bazel/rules_haskell/tests/hs-boot/A.hs.in b/third_party/bazel/rules_haskell/tests/hs-boot/A.hs.in
new file mode 100644
index 000000000000..deeff5470b77
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hs-boot/A.hs.in
@@ -0,0 +1,8 @@
+module A where
+
+import B (TB (..))
+
+newtype TA = MkTA Int
+
+f :: TB -> TA
+f (MkTB x) = MkTA x
diff --git a/third_party/bazel/rules_haskell/tests/hs-boot/BUILD.bazel b/third_party/bazel/rules_haskell/tests/hs-boot/BUILD.bazel
new file mode 100644
index 000000000000..4e69da0e6b34
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hs-boot/BUILD.bazel
@@ -0,0 +1,48 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+genrule(
+    name = "gen-A-boot",
+    srcs = ["A.hs-boot.in"],
+    outs = ["srcs/A.hs-boot"],
+    cmd = "cp $< $@",
+)
+
+genrule(
+    name = "gen-A",
+    srcs = ["A.hs.in"],
+    outs = ["srcs/A.hs"],
+    cmd = "cp $< $@",
+)
+
+haskell_library(
+    name = "hs-boot-lib",
+    srcs = [
+        "srcs/B.hs",
+        ":gen-A",
+        ":gen-A-boot",
+    ],
+    src_strip_prefix = "srcs",
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_test(
+    name = "hs-boot",
+    srcs = [
+        "MA.hs",
+        "MA.hs-boot",
+        "MB.hs",
+        "Main.hs",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":hs-boot-lib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/hs-boot/MA.hs b/third_party/bazel/rules_haskell/tests/hs-boot/MA.hs
new file mode 100644
index 000000000000..4e0128596242
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hs-boot/MA.hs
@@ -0,0 +1,8 @@
+module MA where
+
+import MB (TB (..))
+
+newtype TA = MkTA Int
+
+f :: TB -> TA
+f (MkTB x) = MkTA x
diff --git a/third_party/bazel/rules_haskell/tests/hs-boot/MA.hs-boot b/third_party/bazel/rules_haskell/tests/hs-boot/MA.hs-boot
new file mode 100644
index 000000000000..0ab8c899f2e1
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hs-boot/MA.hs-boot
@@ -0,0 +1,2 @@
+module MA where
+  newtype TA = MkTA Int
diff --git a/third_party/bazel/rules_haskell/tests/hs-boot/MB.hs b/third_party/bazel/rules_haskell/tests/hs-boot/MB.hs
new file mode 100644
index 000000000000..d90d041d578e
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hs-boot/MB.hs
@@ -0,0 +1,8 @@
+module MB where
+
+import {-# SOURCE #-} MA (TA (..))
+
+data TB = MkTB !Int
+
+g :: TA -> TB
+g (MkTA x) = MkTB x
diff --git a/third_party/bazel/rules_haskell/tests/hs-boot/Main.hs b/third_party/bazel/rules_haskell/tests/hs-boot/Main.hs
new file mode 100644
index 000000000000..15c0085fe079
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hs-boot/Main.hs
@@ -0,0 +1,9 @@
+module Main (main) where
+
+import A ()
+import B ()
+import MA ()
+import MB ()
+
+main :: IO ()
+main = putStrLn "hsboot"
diff --git a/third_party/bazel/rules_haskell/tests/hs-boot/srcs/B.hs b/third_party/bazel/rules_haskell/tests/hs-boot/srcs/B.hs
new file mode 100644
index 000000000000..60e1ff5f3edd
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hs-boot/srcs/B.hs
@@ -0,0 +1,8 @@
+module B where
+
+import {-# SOURCE #-} A (TA (..))
+
+data TB = MkTB !Int
+
+g :: TA -> TB
+g (MkTA x) = MkTB x
diff --git a/third_party/bazel/rules_haskell/tests/hsc/BUILD.bazel b/third_party/bazel/rules_haskell/tests/hsc/BUILD.bazel
new file mode 100644
index 000000000000..1e9a07cd5e08
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hsc/BUILD.bazel
@@ -0,0 +1,35 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "hsc-lib",
+    srcs = [
+        "Bar.hsc",
+        "Bar/Baz.hsc",
+        "Flags.hsc",
+        "Foo.hsc",
+    ],
+    compiler_flags = [
+        "-DTHIS_IS_TRUE",
+        "-optP-DTHIS_TOO_IS_TRUE",
+    ],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_test(
+    name = "hsc",
+    srcs = [
+        "BinHsc.hsc",
+        "Main.hs",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":hsc-lib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/hsc/Bar.hsc b/third_party/bazel/rules_haskell/tests/hsc/Bar.hsc
new file mode 100644
index 000000000000..a8bb791513b5
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hsc/Bar.hsc
@@ -0,0 +1,6 @@
+module Bar (hscFiredBar) where
+
+#ifndef _INTERNAL_HSC_DO_NOT_DEFINE_ME
+hscFiredBar :: String
+hscFiredBar = "hscFiredBar"
+#endif
diff --git a/third_party/bazel/rules_haskell/tests/hsc/Bar/Baz.hsc b/third_party/bazel/rules_haskell/tests/hsc/Bar/Baz.hsc
new file mode 100644
index 000000000000..7db172485de5
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hsc/Bar/Baz.hsc
@@ -0,0 +1,6 @@
+module Bar.Baz (hscFiredBaz) where
+
+#ifndef _INTERNAL_HSC_DO_NOT_DEFINE_ME
+hscFiredBaz :: String
+hscFiredBaz = "hscFiredBaz"
+#endif
diff --git a/third_party/bazel/rules_haskell/tests/hsc/BinHsc.hsc b/third_party/bazel/rules_haskell/tests/hsc/BinHsc.hsc
new file mode 100644
index 000000000000..ddfee79c137c
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hsc/BinHsc.hsc
@@ -0,0 +1 @@
+module BinHsc () where
\ No newline at end of file
diff --git a/third_party/bazel/rules_haskell/tests/hsc/Flags.hsc b/third_party/bazel/rules_haskell/tests/hsc/Flags.hsc
new file mode 100644
index 000000000000..b090df576ab4
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hsc/Flags.hsc
@@ -0,0 +1,8 @@
+module Flags (hscFlags) where
+
+#ifdef THIS_IS_TRUE
+#ifdef THIS_TOO_IS_TRUE
+hscFlags :: String
+hscFlags = "hscFlags"
+#endif
+#endif
diff --git a/third_party/bazel/rules_haskell/tests/hsc/Foo.hsc b/third_party/bazel/rules_haskell/tests/hsc/Foo.hsc
new file mode 100644
index 000000000000..2cb726e18110
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hsc/Foo.hsc
@@ -0,0 +1,8 @@
+module Foo (hscFiredFoo) where
+
+#if __GLASGOW_HASKELL__ >= 700
+#ifndef _INTERNAL_HSC_DO_NOT_DEFINE_ME
+hscFiredFoo :: String
+hscFiredFoo = "hscFiredFoo"
+#endif
+#endif
diff --git a/third_party/bazel/rules_haskell/tests/hsc/Main.hs b/third_party/bazel/rules_haskell/tests/hsc/Main.hs
new file mode 100644
index 000000000000..33ff7f10f78c
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/hsc/Main.hs
@@ -0,0 +1,10 @@
+module Main (main) where
+
+import BinHsc ()
+import Foo (hscFiredFoo)
+import Bar (hscFiredBar)
+import Bar.Baz (hscFiredBaz)
+import Flags (hscFlags)
+
+main :: IO ()
+main = putStrLn (hscFiredFoo ++ hscFiredBar ++ hscFiredBaz ++ hscFlags)
diff --git a/third_party/bazel/rules_haskell/tests/indirect-link/BUILD.bazel b/third_party/bazel/rules_haskell/tests/indirect-link/BUILD.bazel
new file mode 100644
index 000000000000..60403a4ef277
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/indirect-link/BUILD.bazel
@@ -0,0 +1,52 @@
+load("@io_tweag_rules_haskell//haskell:haskell.bzl", "haskell_library", "haskell_test", "haskell_toolchain_library")
+
+cc_library(
+    name = "cbits-indirect",
+    srcs = ["cbits/impl.c"],
+)
+
+cc_library(
+    name = "cbits",
+    srcs = ["cbits/intf.c"],
+    deps = ["cbits-indirect"],
+)
+
+haskell_library(
+    name = "mypkg",
+    srcs = ["src/MyModule.hs"],
+    src_strip_prefix = "src",
+    deps = [
+        ":cbits",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_test(
+    name = "indirect-link-static",
+    srcs = ["test/Main.hs"],
+    linkstatic = True,
+    src_strip_prefix = "test",
+    deps = [
+        ":mypkg",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_test(
+    name = "indirect-link-dynamic",
+    srcs = ["test/Main.hs"],
+    linkstatic = False,
+    src_strip_prefix = "test",
+    deps = [
+        ":mypkg",
+        "//tests/hackage:base",
+    ],
+)
+
+test_suite(
+    name = "indirect-link",
+    tests = [
+        ":indirect-link-dynamic",
+        ":indirect-link-static",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/indirect-link/cbits/impl.c b/third_party/bazel/rules_haskell/tests/indirect-link/cbits/impl.c
new file mode 100644
index 000000000000..666ce43962a3
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/indirect-link/cbits/impl.c
@@ -0,0 +1,9 @@
+static int thing;
+
+int real_get_thing(void) {
+  return thing;
+}
+
+void real_set_thing(int value) {
+  thing = value;
+}
diff --git a/third_party/bazel/rules_haskell/tests/indirect-link/cbits/intf.c b/third_party/bazel/rules_haskell/tests/indirect-link/cbits/intf.c
new file mode 100644
index 000000000000..f7a8f5e9f2e8
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/indirect-link/cbits/intf.c
@@ -0,0 +1,10 @@
+extern int real_get_thing(void);
+extern void real_set_thing(int value);
+
+int get_thing(void) {
+  return real_get_thing();
+}
+
+void set_thing(int value) {
+  real_set_thing(value);
+}
diff --git a/third_party/bazel/rules_haskell/tests/indirect-link/src/MyModule.hs b/third_party/bazel/rules_haskell/tests/indirect-link/src/MyModule.hs
new file mode 100644
index 000000000000..4b75cee13989
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/indirect-link/src/MyModule.hs
@@ -0,0 +1,11 @@
+module MyModule where
+
+foreign import ccall get_thing :: IO Int
+
+getThing :: IO Int
+getThing = get_thing
+
+foreign import ccall set_thing :: Int -> IO ()
+
+setThing :: Int -> IO ()
+setThing = set_thing
diff --git a/third_party/bazel/rules_haskell/tests/indirect-link/test/Main.hs b/third_party/bazel/rules_haskell/tests/indirect-link/test/Main.hs
new file mode 100644
index 000000000000..0b3d188d736a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/indirect-link/test/Main.hs
@@ -0,0 +1,9 @@
+module Main (main) where
+
+import qualified MyModule
+
+main :: IO ()
+main = do
+  print =<< MyModule.getThing
+  MyModule.setThing 123
+  print =<< MyModule.getThing
diff --git a/third_party/bazel/rules_haskell/tests/inline_tests.bzl b/third_party/bazel/rules_haskell/tests/inline_tests.bzl
new file mode 100644
index 000000000000..b1ba001c6ec7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/inline_tests.bzl
@@ -0,0 +1,90 @@
+# features sh_inline_test and py_inline_test,
+# which are like their respective builtin rules,
+# but their scripts can be given inline as a string.
+
+load("@bazel_skylib//lib:shell.bzl", "shell")
+
+def quote_make_variables(s):
+    """Quote all genrule “Make” Variables in a string."""
+    return s.replace("$", "$$")
+
+def target_from_string(name, string):
+    """Write a skylark string to a target."""
+    native.genrule(
+        name = name + "-file",
+        outs = [name],
+        # this is exceptionally ugly.
+        cmd = """echo -n {quoted} > $(@)""".format(
+            # but should at least be quoted right
+            quoted = shell.quote(quote_make_variables(string)),
+        ),
+    )
+
+bash_runfiles_boilerplate = """\
+# Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash).
+set -euo pipefail
+if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+  if [[ -f "$0.runfiles_manifest" ]]; then
+    export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+  elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+    export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+  elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+    export RUNFILES_DIR="$0.runfiles"
+  fi
+fi
+if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+  source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+  source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+            "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+else
+  echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+  exit 1
+fi
+# --- end runfiles.bash initialization ---
+"""
+
+def sh_inline_test(name, script, **kwargs):
+    """Like sh_test, but instead of srcs takes the shell script
+    as verbatim bazel string. The bash runfiles are in scope,
+    using `rlocation` works by default.
+    """
+    script_name = name + ".sh"
+    script = bash_runfiles_boilerplate + script
+
+    target_from_string(script_name, script)
+
+    deps = kwargs.pop("deps", [])
+
+    native.sh_test(
+        name = name,
+        srcs = [script_name],
+        deps = ["@bazel_tools//tools/bash/runfiles"] + deps,
+        **kwargs
+    )
+
+python_runfiles_boilerplate = """
+from bazel_tools.tools.python.runfiles import runfiles
+r = runfiles.Create()
+"""
+
+def py_inline_test(name, script, **kwargs):
+    """Like py_test, but instead of srcs takes the shell script
+    as verbatim bazel string. The python runfiles are in scope
+    as the `r` variable. Use `r.Rlocation()`
+    """
+    script_name = name + ".py"
+    script = python_runfiles_boilerplate + script
+
+    target_from_string(script_name, script)
+
+    deps = kwargs.pop("deps", [])
+    srcs = kwargs.pop("srcs", [])
+
+    native.py_test(
+        name = name,
+        srcs = [script_name] + srcs,
+        main = script_name,
+        deps = ["@bazel_tools//tools/python/runfiles"] + deps,
+        **kwargs
+    )
diff --git a/third_party/bazel/rules_haskell/tests/java_classpath/BUILD.bazel b/third_party/bazel/rules_haskell/tests/java_classpath/BUILD.bazel
new file mode 100644
index 000000000000..b2e5b86daeb9
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/java_classpath/BUILD.bazel
@@ -0,0 +1,17 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "java_classpath",
+    srcs = ["Main.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+        "@org_apache_spark_spark_core_2_10//jar",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/java_classpath/Main.hs b/third_party/bazel/rules_haskell/tests/java_classpath/Main.hs
new file mode 100644
index 000000000000..13f7b9d51684
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/java_classpath/Main.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE LambdaCase      #-}
+{-# LANGUAGE TemplateHaskell #-}
+module Main (main) where
+
+import qualified Language.Haskell.TH as TH (runIO)
+import qualified Language.Haskell.TH.Syntax as TH (lift)
+import           System.Environment (lookupEnv)
+
+main :: IO ()
+main = putStrLn $(
+  let ensureClassPath :: IO String
+      ensureClassPath = lookupEnv "CLASSPATH" >>= \case
+        Nothing -> error "CLASSPATH not set when it was expected to be."
+        Just "" -> error "CLASSPATH empty when it was expected to have content."
+        Just cpath -> pure $ "java-classpath at compile time: " ++ cpath
+  in TH.runIO ensureClassPath >>= TH.lift
+  )
diff --git a/third_party/bazel/rules_haskell/tests/lhs/BUILD.bazel b/third_party/bazel/rules_haskell/tests/lhs/BUILD.bazel
new file mode 100644
index 000000000000..e344ee6def1c
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/lhs/BUILD.bazel
@@ -0,0 +1,23 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "lhs-lib",
+    srcs = ["Lib.lhs"],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_test(
+    name = "lhs-bin",
+    srcs = ["Main.lhs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":lhs-lib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/lhs/Lib.lhs b/third_party/bazel/rules_haskell/tests/lhs/Lib.lhs
new file mode 100644
index 000000000000..f37350df70f7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/lhs/Lib.lhs
@@ -0,0 +1,4 @@
+> module Lib (lib) where
+
+> lib :: String
+> lib = "lhs"
diff --git a/third_party/bazel/rules_haskell/tests/lhs/Main.lhs b/third_party/bazel/rules_haskell/tests/lhs/Main.lhs
new file mode 100644
index 000000000000..a9e0659dddf5
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/lhs/Main.lhs
@@ -0,0 +1,6 @@
+> module Main (main) where
+
+> import Lib (lib)
+
+> main :: IO ()
+> main = putStrLn lib
diff --git a/third_party/bazel/rules_haskell/tests/library-deps/BUILD.bazel b/third_party/bazel/rules_haskell/tests/library-deps/BUILD.bazel
new file mode 100644
index 000000000000..d6844e1ac120
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-deps/BUILD.bazel
@@ -0,0 +1,28 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "library-deps",
+    srcs = ["TestLib.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/library-deps/sublib",
+    ],
+)
+
+haskell_test(
+    name = "bin-deps",
+    size = "small",
+    srcs = ["Bin.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/library-deps/sublib",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/library-deps/Bin.hs b/third_party/bazel/rules_haskell/tests/library-deps/Bin.hs
new file mode 100644
index 000000000000..8d12d0bdd23f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-deps/Bin.hs
@@ -0,0 +1,6 @@
+module Main (main) where
+
+import TestSubLib (messageEnd)
+
+main :: IO ()
+main = putStrLn $ messageEnd
diff --git a/third_party/bazel/rules_haskell/tests/library-deps/TestLib.hs b/third_party/bazel/rules_haskell/tests/library-deps/TestLib.hs
new file mode 100644
index 000000000000..fe917fecf3b7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-deps/TestLib.hs
@@ -0,0 +1,9 @@
+module TestLib (testMessage) where
+
+import TestSubLib (messageEnd)
+
+testMessage :: String
+testMessage = "hello " ++ messageEnd
+
+-- Force dynamic linking
+{-# ANN testMessage () #-}
diff --git a/third_party/bazel/rules_haskell/tests/library-deps/sublib/BUILD.bazel b/third_party/bazel/rules_haskell/tests/library-deps/sublib/BUILD.bazel
new file mode 100644
index 000000000000..5f3028e2e02c
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-deps/sublib/BUILD.bazel
@@ -0,0 +1,21 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "sublib",
+    srcs = ["TestSubLib.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":sublib-c",
+        "//tests/hackage:base",
+    ],
+)
+
+cc_library(
+    name = "sublib-c",
+    srcs = ["sublib-c.c"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/library-deps/sublib/TestSubLib.hs b/third_party/bazel/rules_haskell/tests/library-deps/sublib/TestSubLib.hs
new file mode 100644
index 000000000000..1e8d2d475552
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-deps/sublib/TestSubLib.hs
@@ -0,0 +1,7 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module TestSubLib (messageEnd) where
+
+messageEnd :: String
+messageEnd = "world " ++ show (foo 10)
+
+foreign import ccall foo :: Int -> Int
diff --git a/third_party/bazel/rules_haskell/tests/library-deps/sublib/sublib-c.c b/third_party/bazel/rules_haskell/tests/library-deps/sublib/sublib-c.c
new file mode 100644
index 000000000000..2f3be3eb9ed7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-deps/sublib/sublib-c.c
@@ -0,0 +1,3 @@
+int foo(int x) {
+  return x * 2;
+}
diff --git a/third_party/bazel/rules_haskell/tests/library-exports/BUILD.bazel b/third_party/bazel/rules_haskell/tests/library-exports/BUILD.bazel
new file mode 100644
index 000000000000..c88e68ebbee4
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-exports/BUILD.bazel
@@ -0,0 +1,41 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "sublib",
+    srcs = ["TestSubLib.hs"],
+    exports = {"//tests/hackage:containers": "Data.Map as SubLib.Map"},
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:containers",
+    ],
+)
+
+haskell_library(
+    name = "lib",
+    srcs = ["TestLib.hs"],
+    exports = {
+        ":sublib": "TestSubLib",
+        "//tests/hackage:containers": "Data.Map as Lib.Map",
+    },
+    deps = [
+        ":sublib",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_test(
+    name = "library-exports",
+    size = "small",
+    srcs = ["Bin.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":lib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/library-exports/Bin.hs b/third_party/bazel/rules_haskell/tests/library-exports/Bin.hs
new file mode 100644
index 000000000000..b724bb89d1c2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-exports/Bin.hs
@@ -0,0 +1,7 @@
+module Main (main) where
+
+import TestSubLib (messageEnd)
+import Lib.Map
+
+main :: IO ()
+main = print $ Lib.Map.singleton 1 messageEnd
diff --git a/third_party/bazel/rules_haskell/tests/library-exports/TestLib.hs b/third_party/bazel/rules_haskell/tests/library-exports/TestLib.hs
new file mode 100644
index 000000000000..c889c2cc31ac
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-exports/TestLib.hs
@@ -0,0 +1,7 @@
+module TestLib (testMessage) where
+
+import TestSubLib (messageEnd)
+import SubLib.Map
+
+testMessage :: String
+testMessage = "hello " ++ messageEnd
diff --git a/third_party/bazel/rules_haskell/tests/library-exports/TestSubLib.hs b/third_party/bazel/rules_haskell/tests/library-exports/TestSubLib.hs
new file mode 100644
index 000000000000..2e1f3f983f40
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-exports/TestSubLib.hs
@@ -0,0 +1,5 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module TestSubLib (messageEnd) where
+
+messageEnd :: String
+messageEnd = "world"
diff --git a/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/BUILD.bazel b/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/BUILD.bazel
new file mode 100644
index 000000000000..0b7cae3d7a65
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/BUILD.bazel
@@ -0,0 +1,112 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+load("//tests:inline_tests.bzl", "sh_inline_test")
+load(":get_library_files.bzl", "get_libraries_as_runfiles")
+
+# test whether `linkstatic` works as expected
+package(default_testonly = 1)
+
+# only .a files
+haskell_library(
+    name = "library-static-only",
+    srcs = ["Lib.hs"],
+    linkstatic = True,  # <--
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:bytestring",
+    ],
+)
+
+# both .a and .so files
+haskell_library(
+    name = "library-static-and-dynamic",
+    srcs = ["Lib.hs"],
+    linkstatic = False,  # <--
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:bytestring",
+    ],
+)
+
+# extract all libraries from the haskell_library
+get_libraries_as_runfiles(
+    name = "library-static-only-libraries",
+    library = ":library-static-only",
+)
+
+get_libraries_as_runfiles(
+    name = "library-static-and-dynamic-libraries",
+    library = ":library-static-and-dynamic",
+)
+
+# sh_test’s `data` doesn’t add stuff to runfiles :(
+# sh_library can bundle different targets as runfiles for sh_test
+# TODO(Profpatsch): add functionality to sh_inline_test by default?
+sh_library(
+    name = "bundled-dependency-files-static-only",
+    data = [":library-static-only-libraries"],
+)
+
+sh_library(
+    name = "bundled-dependency-files-static-and-dynamic",
+    data = [":library-static-and-dynamic-libraries"],
+)
+
+# ensure that linkstatic=True only creates only .a, no .so
+sh_inline_test(
+    name = "library-linkstatic-flag",
+    size = "small",
+    # pass the file names as arguments
+    args = ["$(rootpaths :library-static-only-libraries)"],
+    data = [
+        # for rootpaths
+        ":library-static-only-libraries",
+        # to actually get the files …
+        ":bundled-dependency-files-static-only",
+    ],
+    script = """
+set -euo pipefail
+for f in "$@"; do
+    if ! [[ "$f" =~ .a$ ]]; then
+      echo "not a static library: $f"
+      exit 1
+    fi
+done
+""",
+)
+
+# test whether .so is linked dynamically and .a statically
+sh_inline_test(
+    name = "test-libraries-static-and-dynamic",
+    size = "small",
+    # pass the file names as arguments
+    args = ["$(rootpaths :library-static-and-dynamic-libraries)"],
+    data = [
+        # for rootpaths
+        ":library-static-and-dynamic-libraries",
+        # to actually get the files …
+        ":bundled-dependency-files-static-and-dynamic",
+    ],
+    script = """
+set -euo pipefail
+is_dynamic () {
+    # taken from https://github.com/NixOS/nixpkgs/blob/0b3f50f844e2a6b507b18d7c5259bb850b382f87/pkgs/build-support/setup-hooks/auto-patchelf.sh#L167-L170
+    readelf -l -- "$1" | grep -q "^ *INTERP\\>"
+}
+
+for f in "$@"; do
+    if [[ "$f" =~ .a$ ]] && is_dynamic "$f"; then
+        echo "should be a static executable: $f"
+        exit 1
+    fi
+    if [[ "$f" =~ .so$ ]] && ! is_dynamic "$f"; then
+        echo "should be a dynamic executable: $f"
+        exit 1
+    fi
+done
+""",
+)
diff --git a/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/Lib.hs b/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/Lib.hs
new file mode 100644
index 000000000000..66a2161a19c4
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/Lib.hs
@@ -0,0 +1,5 @@
+module Lib (message) where
+
+import qualified Data.ByteString.Char8 as B
+
+message = B.pack "hello, world"
diff --git a/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/Main.hs b/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/Main.hs
new file mode 100644
index 000000000000..2048dbdecd9a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/Main.hs
@@ -0,0 +1,3 @@
+module Main where
+
+main = putStrLn "hello world"
diff --git a/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/get_library_files.bzl b/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/get_library_files.bzl
new file mode 100644
index 000000000000..31702bfcfa07
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-linkstatic-flag/get_library_files.bzl
@@ -0,0 +1,28 @@
+load(
+    "@io_tweag_rules_haskell//haskell:providers.bzl",
+    "HaskellInfo",
+    "HaskellLibraryInfo",
+)
+load("//haskell:private/set.bzl", "set")
+
+def _get_libraries_as_runfiles_impl(ctx):
+    """Extract all library files from a haskell_library target
+    and put them in this target’s files"""
+    bi = ctx.attr.library[HaskellInfo]
+    return [DefaultInfo(
+        # not necessarily complete
+        files = depset(
+            direct = bi.static_libraries,
+            transitive = [set.to_depset(bi.dynamic_libraries)],
+        ),
+    )]
+
+get_libraries_as_runfiles = rule(
+    _get_libraries_as_runfiles_impl,
+    attrs = {
+        "library": attr.label(
+            mandatory = True,
+            providers = [HaskellInfo, HaskellLibraryInfo],
+        ),
+    },
+)
diff --git a/third_party/bazel/rules_haskell/tests/library-with-cbits/AddOne.hsc b/third_party/bazel/rules_haskell/tests/library-with-cbits/AddOne.hsc
new file mode 100644
index 000000000000..b4c446d412c7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-cbits/AddOne.hsc
@@ -0,0 +1,3 @@
+module AddOne where
+
+foreign import ccall "c_add_one" addOne :: Int -> Int
diff --git a/third_party/bazel/rules_haskell/tests/library-with-cbits/AddOne2.hs b/third_party/bazel/rules_haskell/tests/library-with-cbits/AddOne2.hs
new file mode 100644
index 000000000000..f2088f1c6600
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-cbits/AddOne2.hs
@@ -0,0 +1,7 @@
+module AddOne2
+  ( addOne2
+  ) where
+
+import AddOne
+
+addOne2 = addOne
diff --git a/third_party/bazel/rules_haskell/tests/library-with-cbits/BUILD.bazel b/third_party/bazel/rules_haskell/tests/library-with-cbits/BUILD.bazel
new file mode 100644
index 000000000000..c320502ebf3f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-cbits/BUILD.bazel
@@ -0,0 +1,36 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_toolchain_library",
+)
+
+haskell_library(
+    name = "library-with-cbits",
+    srcs = ["AddOne.hsc"],
+    linkstatic = False,
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/data:ourclibrary",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_library(
+    name = "library-with-cbits-indirect",
+    srcs = ["AddOne2.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":library-with-cbits",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_library(
+    name = "library-with-cbits-static",
+    srcs = ["AddOne.hsc"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/data:ourclibrary-static",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/library-with-includes/BUILD.bazel b/third_party/bazel/rules_haskell/tests/library-with-includes/BUILD.bazel
new file mode 100644
index 000000000000..37b5a2ee986e
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-includes/BUILD.bazel
@@ -0,0 +1,29 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(default_testonly = 1)
+
+genrule(
+    name = "gen-a",
+    outs = ["a.h"],
+    cmd = "echo '#define A 42' >> $@",
+)
+
+cc_library(
+    name = "clib",
+    hdrs = [
+        "b.h",
+        ":gen-a",
+    ],
+)
+
+haskell_library(
+    name = "library-with-includes",
+    srcs = ["Lib.hs"],
+    deps = [
+        ":clib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/library-with-includes/Lib.hs b/third_party/bazel/rules_haskell/tests/library-with-includes/Lib.hs
new file mode 100644
index 000000000000..1ca98d615c4a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-includes/Lib.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE CPP #-}
+module Lib (x) where
+
+#include "tests/library-with-includes/a.h"
+#include "tests/library-with-includes/b.h"
+
+x :: Int
+x = A + B
diff --git a/third_party/bazel/rules_haskell/tests/library-with-includes/b.h b/third_party/bazel/rules_haskell/tests/library-with-includes/b.h
new file mode 100644
index 000000000000..3425137bf9cf
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-includes/b.h
@@ -0,0 +1 @@
+#define B 17
diff --git a/third_party/bazel/rules_haskell/tests/library-with-sysdeps/BUILD.bazel b/third_party/bazel/rules_haskell/tests/library-with-sysdeps/BUILD.bazel
new file mode 100644
index 000000000000..0e34f688fd69
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-sysdeps/BUILD.bazel
@@ -0,0 +1,32 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "library-with-sysdeps",
+    srcs = ["Lib.hs"],
+    tags = ["requires_zlib"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "@zlib",
+    ],
+)
+
+haskell_test(
+    name = "bin",
+    srcs = ["Main.hs"],
+    expected_covered_expressions_percentage = 100,
+    tags = [
+        "coverage-compatible",
+        "requires_zlib",
+    ],
+    deps = [
+        ":library-with-sysdeps",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/library-with-sysdeps/Lib.hs b/third_party/bazel/rules_haskell/tests/library-with-sysdeps/Lib.hs
new file mode 100644
index 000000000000..771dd7faf10f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-sysdeps/Lib.hs
@@ -0,0 +1,8 @@
+module Lib (crc) where
+
+import Foreign.Ptr
+import Foreign.C.Types
+
+foreign import ccall crc32 :: CLong -> Ptr () -> CInt -> IO ()
+
+crc = crc32 0 nullPtr 0
diff --git a/third_party/bazel/rules_haskell/tests/library-with-sysdeps/Main.hs b/third_party/bazel/rules_haskell/tests/library-with-sysdeps/Main.hs
new file mode 100644
index 000000000000..b892de88c4a7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-sysdeps/Main.hs
@@ -0,0 +1,5 @@
+module Main where
+
+import Lib (crc)
+
+main = crc
diff --git a/third_party/bazel/rules_haskell/tests/library-with-sysincludes/BUILD.bazel b/third_party/bazel/rules_haskell/tests/library-with-sysincludes/BUILD.bazel
new file mode 100644
index 000000000000..ddd5de65198c
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-sysincludes/BUILD.bazel
@@ -0,0 +1,86 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(default_testonly = 1)
+
+genrule(
+    name = "genrule-header",
+    outs = [
+        "include/foo.h",
+    ],
+    cmd = "touch $(location include/foo.h)",
+)
+
+# A locally-defined replica of @zlib.dev//:zlib.
+# Since that shared library lives in another package, we must
+# use an absolute path for strip_include_prefix.
+cc_library(
+    name = "zlib",
+    hdrs = ["@zlib.dev//:include"],
+    strip_include_prefix = "/external/zlib.dev/include",
+    tags = ["requires_zlib"],
+    deps = ["@zlib"],
+)
+
+cc_library(
+    name = "zlib-with-genrule-header",
+    hdrs = [":genrule-header"],
+    strip_include_prefix = "include",
+    tags = ["requires_zlib"],
+)
+
+haskell_library(
+    name = "intermediate-library",
+    srcs = ["IntLib.hsc"],
+    tags = ["requires_zlib"],
+    deps = [
+        ":zlib",
+        ":zlib-with-genrule-header",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_library(
+    name = "library-with-sysincludes",
+    srcs = [
+        "Lib.hs",
+        "TH.hs",
+    ],
+    tags = ["requires_zlib"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":intermediate-library",
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+    ],
+)
+
+# Replicate the above example, but use the externally-defined
+# cc_library rule.
+haskell_library(
+    name = "intermediate-library-other",
+    srcs = ["IntLib.hsc"],
+    tags = ["requires_zlib"],
+    deps = [
+        ":zlib-with-genrule-header",
+        "//tests/hackage:base",
+        "@zlib.dev//:zlib",
+    ],
+)
+
+haskell_library(
+    name = "library-with-sysincludes-other",
+    srcs = [
+        "Lib.hs",
+        "TH.hs",
+    ],
+    tags = ["requires_zlib"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":intermediate-library-other",
+        "//tests/hackage:base",
+        "//tests/hackage:template-haskell",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/library-with-sysincludes/IntLib.hsc b/third_party/bazel/rules_haskell/tests/library-with-sysincludes/IntLib.hsc
new file mode 100644
index 000000000000..cf31e3d9a844
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-sysincludes/IntLib.hsc
@@ -0,0 +1,15 @@
+{-# LANGUAGE CPP #-}
+
+module IntLib (crc) where
+
+import Foreign.Ptr
+import Foreign.C.Types
+
+#include <zlib.h>
+#include "foo.h"
+
+foreign import ccall crc32 :: CLong -> Ptr () -> CInt -> IO ()
+
+crc = crc32 0 nullPtr 0
+
+z = #{size struct gz_header_s}
diff --git a/third_party/bazel/rules_haskell/tests/library-with-sysincludes/Lib.hs b/third_party/bazel/rules_haskell/tests/library-with-sysincludes/Lib.hs
new file mode 100644
index 000000000000..55b1860fdddd
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-sysincludes/Lib.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Lib (bar) where
+
+import TH (foo)
+
+bar :: IO ()
+bar = $foo
diff --git a/third_party/bazel/rules_haskell/tests/library-with-sysincludes/TH.hs b/third_party/bazel/rules_haskell/tests/library-with-sysincludes/TH.hs
new file mode 100644
index 000000000000..45929ec84040
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/library-with-sysincludes/TH.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module TH (foo) where
+
+import IntLib (crc)
+import Language.Haskell.TH
+
+foo :: Q Exp
+foo = [| crc |]
diff --git a/third_party/bazel/rules_haskell/tests/multi_repl/BUILD.bazel b/third_party/bazel/rules_haskell/tests/multi_repl/BUILD.bazel
new file mode 100644
index 000000000000..b2edce37ab0f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/multi_repl/BUILD.bazel
@@ -0,0 +1,16 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_repl",
+)
+
+haskell_repl(
+    name = "c_only_repl",
+    # To only load :c by source.
+    experimental_from_source = ["//tests/multi_repl/bc:c"],
+    deps = ["//tests/multi_repl/bc:c"],
+)
+
+haskell_repl(
+    name = "c_multi_repl",
+    deps = ["//tests/multi_repl/bc:c"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/multi_repl/a/BUILD.bazel b/third_party/bazel/rules_haskell/tests/multi_repl/a/BUILD.bazel
new file mode 100644
index 000000000000..7d17e834a9cd
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/multi_repl/a/BUILD.bazel
@@ -0,0 +1,17 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+haskell_library(
+    name = "a",
+    srcs = [
+        "src/A/A.hs",
+    ],
+    src_strip_prefix = "src",
+    deps = [
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/multi_repl/a/src/A/A.hs b/third_party/bazel/rules_haskell/tests/multi_repl/a/src/A/A.hs
new file mode 100644
index 000000000000..92eb0e60759a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/multi_repl/a/src/A/A.hs
@@ -0,0 +1,4 @@
+module A.A ( a ) where
+
+a :: ()
+a = ()
diff --git a/third_party/bazel/rules_haskell/tests/multi_repl/bc/BUILD.bazel b/third_party/bazel/rules_haskell/tests/multi_repl/bc/BUILD.bazel
new file mode 100644
index 000000000000..98372a4a1925
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/multi_repl/bc/BUILD.bazel
@@ -0,0 +1,30 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+haskell_library(
+    name = "b",
+    srcs = [
+        "src/BC/B.hs",
+    ],
+    src_strip_prefix = "src",
+    deps = [
+        "//tests/hackage:base",
+        "//tests/multi_repl/a",
+    ],
+)
+
+haskell_library(
+    name = "c",
+    srcs = [
+        "src/BC/C.hs",
+    ],
+    src_strip_prefix = "src",
+    deps = [
+        ":b",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/multi_repl/bc/src/BC/B.hs b/third_party/bazel/rules_haskell/tests/multi_repl/bc/src/BC/B.hs
new file mode 100644
index 000000000000..b86223c41ea7
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/multi_repl/bc/src/BC/B.hs
@@ -0,0 +1,6 @@
+module BC.B ( b ) where
+
+import A.A ( a )
+
+b :: ()
+b = a
diff --git a/third_party/bazel/rules_haskell/tests/multi_repl/bc/src/BC/C.hs b/third_party/bazel/rules_haskell/tests/multi_repl/bc/src/BC/C.hs
new file mode 100644
index 000000000000..6e6f31f0c80a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/multi_repl/bc/src/BC/C.hs
@@ -0,0 +1,6 @@
+module BC.C ( c ) where
+
+import BC.B ( b )
+
+c :: ()
+c = b
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash-binary/BUILD.bazel b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/BUILD.bazel
new file mode 100644
index 000000000000..d64a8909c5f5
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/BUILD.bazel
@@ -0,0 +1,15 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+    "haskell_toolchain_library",
+)
+
+haskell_test(
+    name = "bin",
+    srcs = ["Main.hs"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/package-id-clash-binary/a:foo",
+        "//tests/package-id-clash-binary/b:foo",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash-binary/Main.hs b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/Main.hs
new file mode 100644
index 000000000000..0c341e6f47d2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import Foo
+import Baz
+
+main = print $ x + y
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash-binary/a/BUILD.bazel b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/a/BUILD.bazel
new file mode 100644
index 000000000000..02c3a6b36d3f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/a/BUILD.bazel
@@ -0,0 +1,12 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_toolchain_library",
+)
+
+haskell_library(
+    name = "foo",
+    srcs = ["Foo.hs"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash-binary/a/Foo.hs b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/a/Foo.hs
new file mode 100644
index 000000000000..ecac0701a07d
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/a/Foo.hs
@@ -0,0 +1,3 @@
+module Foo where
+
+x = 2
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash-binary/b/BUILD.bazel b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/b/BUILD.bazel
new file mode 100644
index 000000000000..4bf734af5dd1
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/b/BUILD.bazel
@@ -0,0 +1,12 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_toolchain_library",
+)
+
+haskell_library(
+    name = "foo",
+    srcs = ["Baz.hs"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash-binary/b/Baz.hs b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/b/Baz.hs
new file mode 100644
index 000000000000..3159fda6b00a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash-binary/b/Baz.hs
@@ -0,0 +1,3 @@
+module Baz where
+
+y = 2
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash/BUILD.bazel b/third_party/bazel/rules_haskell/tests/package-id-clash/BUILD.bazel
new file mode 100644
index 000000000000..9c1b91cf9b83
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash/BUILD.bazel
@@ -0,0 +1,24 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "sublib",
+    srcs = ["Foo.hs"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_library(
+    name = "lib",
+    srcs = ["Baz.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":sublib",
+        "//tests/hackage:base",
+        "//tests/package-id-clash/sublib",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash/Baz.hs b/third_party/bazel/rules_haskell/tests/package-id-clash/Baz.hs
new file mode 100644
index 000000000000..718a2dd99241
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash/Baz.hs
@@ -0,0 +1,7 @@
+module Baz (baz) where
+
+import Foo (foo)
+import Bar (bar)
+
+baz :: Int
+baz = foo + bar
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash/Foo.hs b/third_party/bazel/rules_haskell/tests/package-id-clash/Foo.hs
new file mode 100644
index 000000000000..f325b2efcfbb
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash/Foo.hs
@@ -0,0 +1,4 @@
+module Foo (foo) where
+
+foo :: Int
+foo = 5
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash/sublib/BUILD.bazel b/third_party/bazel/rules_haskell/tests/package-id-clash/sublib/BUILD.bazel
new file mode 100644
index 000000000000..e5d277c2a254
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash/sublib/BUILD.bazel
@@ -0,0 +1,13 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "sublib",
+    srcs = ["Bar.hs"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/package-id-clash/sublib/Bar.hs b/third_party/bazel/rules_haskell/tests/package-id-clash/sublib/Bar.hs
new file mode 100644
index 000000000000..6fbd9711f58b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-id-clash/sublib/Bar.hs
@@ -0,0 +1,4 @@
+module Bar (bar) where
+
+bar :: Int
+bar = 6
diff --git a/third_party/bazel/rules_haskell/tests/package-name/BUILD.bazel b/third_party/bazel/rules_haskell/tests/package-name/BUILD.bazel
new file mode 100644
index 000000000000..9d187f083636
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-name/BUILD.bazel
@@ -0,0 +1,28 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+# Tests around our use of package names.
+package(default_testonly = 1)
+
+haskell_library(
+    # The character "Z" should be untouched in the GHC package name.
+    # However, underscores (which are not legal) should be turned into dashes.
+    name = "lib-a_Z",
+    srcs = ["Lib.hs"],
+    version = "1.2.3.4",
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_test(
+    name = "bin",
+    size = "small",
+    srcs = ["Main.hs"],
+    version = "0.0.0",  # This flags triggers the `MIN_VERSION` macro generation
+    deps = [
+        ":lib-a_Z",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/package-name/Lib.hs b/third_party/bazel/rules_haskell/tests/package-name/Lib.hs
new file mode 100644
index 000000000000..e5e06b414ba0
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-name/Lib.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE CPP #-}
+module Lib (foo, libPackageKey) where
+
+foo :: Integer
+foo = 42
+
+libPackageKey :: String
+libPackageKey = CURRENT_PACKAGE_KEY
diff --git a/third_party/bazel/rules_haskell/tests/package-name/Main.hs b/third_party/bazel/rules_haskell/tests/package-name/Main.hs
new file mode 100644
index 000000000000..b1e73dcbf716
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/package-name/Main.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE PackageImports #-}
+module Main (main) where
+
+import Control.Monad (when)
+
+-- Test the PackageImports extension.
+import "lib-a-Z" Lib
+
+-- Test macros that GHC creates automatically based on the package version.
+versionHighEnoughTest, versionTooLowTest :: Bool
+#if MIN_VERSION_lib_a_Z(1,2,3)
+versionHighEnoughTest = True
+#else
+versionHighEnoughTest = False
+#endif
+
+#if MIN_VERSION_lib_a_Z(1,2,4)
+versionTooLowTest = False
+#else
+versionTooLowTest = True
+#endif
+
+check :: (Show a, Eq a) => a -> a -> IO ()
+check x y = when (x /= y) $ error $ "Failed check: " ++ show (x, y)
+
+main :: IO ()
+main = do
+    check foo 42
+    check VERSION_lib_a_Z "1.2.3.4"
+    check libPackageKey "testsZSpackage-nameZSlib-a-ZZ"
+    check versionHighEnoughTest True
+    check versionTooLowTest True
diff --git a/third_party/bazel/rules_haskell/tests/repl-flags/BUILD.bazel b/third_party/bazel/rules_haskell/tests/repl-flags/BUILD.bazel
new file mode 100644
index 000000000000..8fd099dc4d12
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-flags/BUILD.bazel
@@ -0,0 +1,45 @@
+load("@io_tweag_rules_haskell//haskell:haskell.bzl", "haskell_test")
+
+package(default_testonly = 1)
+
+# This test the `compiler` flags in many ways:
+# - the test needs to be built with `-DTESTS_TOOLCHAIN_COMPILER_FLAGS`, provided by the toolchain attribute `compiler_flags`
+# - the test needs `-XOverloadedStrings`, provided by this rule `compiler_flags`
+# - toolchain and rule `compiler_flags` are additive, else one of the previous test will fail
+# - the ordering is as such as rule flags are more important that toolchain flags
+
+# This rule must build correctly (using `bazel build`), but also as a repl (using `bazel run //tests/repl-flags:compiler_flags@repl`)
+haskell_test(
+    name = "compiler_flags",
+    srcs = ["CompilerFlags.hs"],
+
+    # This also ensure that local `compiler_flags` does not override the `global ones`
+    compiler_flags = ["-XOverloadedStrings"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:bytestring",
+    ],
+)
+
+# This test the `repl_ghci_args` flags in a similar way
+# - the test needs to be built with `-DTESTS_TOOLCHAIN_REPL_FLAGS`, provided by the toolchain attribute `compiler_flags`
+# - the test needs `-XOverloadedStrings`, provided by toolchain `repl_ghci_args`
+# - toolchain and rule `compiler_flags` are additive, else one of the previous test will fail
+# - the ordering is as such as rule flags are more important that
+#    toolchain flags and that repl flags are more important that
+#    copmiler flags
+
+# This rule must build correctly (using `bazel build`), but also as a repl (using `bazel run //tests/repl-flags:compiler_flags@repl`). The final result between the repl and the binary must be different
+haskell_test(
+    name = "repl_flags",
+    srcs = ["ReplFlags.hs"],
+
+    # This also ensure that local `repl_flags` does not override the `global ones`
+    repl_ghci_args = ["-DTESTS_TOOLCHAIN_REPL_FLAGS"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:bytestring",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/repl-flags/CompilerFlags.hs b/third_party/bazel/rules_haskell/tests/repl-flags/CompilerFlags.hs
new file mode 100644
index 000000000000..5470fe11b57c
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-flags/CompilerFlags.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE CPP #-}
+
+module Main where
+
+import Data.ByteString
+
+#ifdef TESTS_TOOLCHAIN_COMPILER_FLAGS
+main = print ("hello" :: ByteString)
+#endif
+
diff --git a/third_party/bazel/rules_haskell/tests/repl-flags/ReplFlags.hs b/third_party/bazel/rules_haskell/tests/repl-flags/ReplFlags.hs
new file mode 100644
index 000000000000..137ec93a72f4
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-flags/ReplFlags.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE CPP #-}
+module Main where
+
+import Data.ByteString
+
+-- Ensure that `compiler-flags` are correctly set
+#ifdef TESTS_TOOLCHAIN_COMPILER_FLAGS
+main = print "hello"
+#endif
+
+-- Ensure that `repl_ghci_args` are correctly set
+-- OverloadedString is passed by toolchain
+-- The CPP constant is unset by toolchain and set by rule, so ordering must be ensured
+#ifdef TESTS_TOOLCHAIN_REPL_FLAGS
+foo = ("world" :: ByteString)
+#endif
+
diff --git a/third_party/bazel/rules_haskell/tests/repl-name-conflicts/BUILD.bazel b/third_party/bazel/rules_haskell/tests/repl-name-conflicts/BUILD.bazel
new file mode 100644
index 000000000000..ea2366cf3e08
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-name-conflicts/BUILD.bazel
@@ -0,0 +1,15 @@
+load("@io_tweag_rules_haskell//haskell:haskell.bzl", "haskell_library")
+
+package(default_testonly = 1)
+
+# The module in this library hides a lot of identifiers from Prelude and other
+# modules used in the repl init script.
+# This shouldn't break the @repl target
+haskell_library(
+    name = "lib",
+    srcs = ["PreludeShadowing.hs"],
+    deps = [
+        "//tests/hackage:base",
+        "//tests/hackage:bytestring",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/repl-name-conflicts/PreludeShadowing.hs b/third_party/bazel/rules_haskell/tests/repl-name-conflicts/PreludeShadowing.hs
new file mode 100644
index 000000000000..02ecd02684ba
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-name-conflicts/PreludeShadowing.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module PreludeShadowing where
+
+import           Data.ByteString
+
+(>>=) :: ByteString -> ByteString -> ByteString
+_ >>= _ = "blah"
+
+stdin :: ByteString
+stdin = "stdin"
diff --git a/third_party/bazel/rules_haskell/tests/repl-targets/BUILD.bazel b/third_party/bazel/rules_haskell/tests/repl-targets/BUILD.bazel
new file mode 100644
index 000000000000..2fd78bfd6621
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-targets/BUILD.bazel
@@ -0,0 +1,81 @@
+load("@io_tweag_rules_haskell//haskell:c2hs.bzl", "c2hs_library")
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+genrule(
+    name = "codegen",
+    outs = [
+        "Gen.hs",
+    ],
+    cmd = """
+  echo "module Gen (gen) where" >> $(location :Gen.hs)
+  echo "gen :: String" >> $(location :Gen.hs)
+  echo "gen = \\"gen\\"" >> $(location :Gen.hs)
+""",
+)
+
+c2hs_library(
+    name = "chs",
+    srcs = ["Chs.chs"],
+    tags = ["requires_c2hs"],
+)
+
+haskell_library(
+    name = "hs-lib",
+    srcs = [
+        "Foo.hs",
+        "Hsc.hsc",
+        ":chs",
+        ":codegen",
+    ],
+    tags = [
+        "requires_hackage",
+        "requires_zlib",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/data:ourclibrary",
+        "//tests/hackage:array",
+        "//tests/hackage:base",
+        "@zlib",
+    ],
+)
+
+haskell_library(
+    name = "hs-lib-bad",
+    srcs = [
+        "Bad.hs",
+    ],
+    tags = [
+        "manual",
+        "requires_zlib",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//tests/data:ourclibrary",
+        "//tests/hackage:base",
+        "@hackage//:array",
+        "@zlib",
+    ],
+)
+
+haskell_library(
+    name = "QuuxLib",
+    srcs = ["QuuxLib.hs"],
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_test(
+    name = "hs-bin",
+    srcs = ["Quux.hs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":QuuxLib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/repl-targets/Bad.hs b/third_party/bazel/rules_haskell/tests/repl-targets/Bad.hs
new file mode 100644
index 000000000000..2889be2ba757
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-targets/Bad.hs
@@ -0,0 +1,6 @@
+module Bad
+  ( bad )
+where
+
+bad :: Into
+bad = "foo"
diff --git a/third_party/bazel/rules_haskell/tests/repl-targets/Chs.chs b/third_party/bazel/rules_haskell/tests/repl-targets/Chs.chs
new file mode 100644
index 000000000000..c66bae7d60fd
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-targets/Chs.chs
@@ -0,0 +1,6 @@
+module Chs
+  ( baz )
+where
+
+baz :: String
+baz = "baz"
diff --git a/third_party/bazel/rules_haskell/tests/repl-targets/Foo.hs b/third_party/bazel/rules_haskell/tests/repl-targets/Foo.hs
new file mode 100644
index 000000000000..5e5138418d5d
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-targets/Foo.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+module Foo (foo) where
+
+foreign import ccall "c_add_one"
+  c_add_one :: Int -> Int
+
+foo :: Int -> Int
+foo = (+ 5) . c_add_one
diff --git a/third_party/bazel/rules_haskell/tests/repl-targets/Hsc.hsc b/third_party/bazel/rules_haskell/tests/repl-targets/Hsc.hsc
new file mode 100644
index 000000000000..d4e3d6605c00
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-targets/Hsc.hsc
@@ -0,0 +1,8 @@
+module Hsc
+  ( bar )
+where
+
+#ifndef _INTERNAL_HSC_DO_NOT_DEFINE_ME
+bar :: String
+bar = "bar"
+#endif
diff --git a/third_party/bazel/rules_haskell/tests/repl-targets/Quux.hs b/third_party/bazel/rules_haskell/tests/repl-targets/Quux.hs
new file mode 100644
index 000000000000..2e67d221fb22
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-targets/Quux.hs
@@ -0,0 +1,6 @@
+module Main (main) where
+
+import QuuxLib (message)
+
+main :: IO ()
+main = putStrLn message
diff --git a/third_party/bazel/rules_haskell/tests/repl-targets/QuuxLib.hs b/third_party/bazel/rules_haskell/tests/repl-targets/QuuxLib.hs
new file mode 100644
index 000000000000..d321de2adda3
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-targets/QuuxLib.hs
@@ -0,0 +1,4 @@
+module QuuxLib (message) where
+
+message :: String
+message = "Hello GHCi!"
diff --git a/third_party/bazel/rules_haskell/tests/repl-targets/src/Bar.hs b/third_party/bazel/rules_haskell/tests/repl-targets/src/Bar.hs
new file mode 100644
index 000000000000..18fe359e0a2a
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-targets/src/Bar.hs
@@ -0,0 +1,4 @@
+module Bar (bar) where
+
+bar :: Int
+bar = 4
diff --git a/third_party/bazel/rules_haskell/tests/repl-targets/src/Baz.hsc b/third_party/bazel/rules_haskell/tests/repl-targets/src/Baz.hsc
new file mode 100644
index 000000000000..984ec4ff0ad9
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/repl-targets/src/Baz.hsc
@@ -0,0 +1,4 @@
+module Baz (baz) where
+
+baz :: Int
+baz = 8
diff --git a/third_party/bazel/rules_haskell/tests/rule_test_exe.bzl b/third_party/bazel/rules_haskell/tests/rule_test_exe.bzl
new file mode 100644
index 000000000000..fdecbb385871
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/rule_test_exe.bzl
@@ -0,0 +1,14 @@
+load("@bazel_tools//tools/build_rules:test_rules.bzl", "rule_test")
+
+def rule_test_exe(generates, **kwargs):
+    """
+        Like 'rule_test' but appends ".exe" to the elements of
+        "generates".
+    """
+
+    new_generates = select({
+        "@bazel_tools//src/conditions:windows": [e + ".exe" for e in generates],
+        "//conditions:default": generates,
+    })
+
+    rule_test(generates = new_generates, **kwargs)
diff --git a/third_party/bazel/rules_haskell/tests/run-start-script.sh b/third_party/bazel/rules_haskell/tests/run-start-script.sh
new file mode 100755
index 000000000000..6803581f0010
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/run-start-script.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# Run the start script in its own workspace
+# and build the example binary target.
+
+set -e
+
+pwd=$(pwd)
+cd $(mktemp -d)
+$pwd/start
+
+# Copy the bazel configuration, this is only useful for CI
+mkdir tools
+cp $pwd/.bazelrc .bazelrc
+
+# Set Nixpkgs in environment variable to avoid hardcoding it in
+# start script itself.
+
+# overrides the used rules_haskell, because
+# when we're testing the start on a feature branch (CI),
+# the latest rules_haskell version doesn't always work.
+# If on the branch we update Bazel to a version with breaking
+# changes, then we need to adapt to those changes in the branch.
+# Which in turn means the start script should pull in those changes too.
+
+NIX_PATH=nixpkgs=$pwd/nixpkgs/default.nix \
+  bazel build \
+  --config=ci \
+  --override_repository=io_tweag_rules_haskell=$pwd \
+  //:example
diff --git a/third_party/bazel/rules_haskell/tests/scripts/exec.sh b/third_party/bazel/rules_haskell/tests/scripts/exec.sh
new file mode 100755
index 000000000000..2a6c2a810041
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/scripts/exec.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+set -e
+
+exec "$1"
diff --git a/third_party/bazel/rules_haskell/tests/textual-hdrs/BUILD.bazel b/third_party/bazel/rules_haskell/tests/textual-hdrs/BUILD.bazel
new file mode 100644
index 000000000000..e66f497c77f2
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/textual-hdrs/BUILD.bazel
@@ -0,0 +1,17 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_test",
+)
+
+package(default_testonly = 1)
+
+haskell_test(
+    name = "textual-hdrs",
+    srcs = [
+        "Main.hs",
+        "include/main_definition.h",
+    ],
+    compiler_flags = ["-Itests/textual-hdrs/include"],
+    visibility = ["//visibility:public"],
+    deps = ["//tests/hackage:base"],
+)
diff --git a/third_party/bazel/rules_haskell/tests/textual-hdrs/Main.hs b/third_party/bazel/rules_haskell/tests/textual-hdrs/Main.hs
new file mode 100644
index 000000000000..f741091275d8
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/textual-hdrs/Main.hs
@@ -0,0 +1,5 @@
+{-# LANGUAGE CPP #-}
+
+module Main (main) where
+
+#include "main_definition.h"
diff --git a/third_party/bazel/rules_haskell/tests/textual-hdrs/include/main_definition.h b/third_party/bazel/rules_haskell/tests/textual-hdrs/include/main_definition.h
new file mode 100644
index 000000000000..071eeddd8706
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/textual-hdrs/include/main_definition.h
@@ -0,0 +1,2 @@
+main :: IO ()
+main = putStrLn "extras-included-during-comp"
diff --git a/third_party/bazel/rules_haskell/tests/two-libs/BUILD.bazel b/third_party/bazel/rules_haskell/tests/two-libs/BUILD.bazel
new file mode 100644
index 000000000000..d69dba515c2d
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/two-libs/BUILD.bazel
@@ -0,0 +1,42 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+
+package(
+    default_testonly = 1,
+    default_visibility = ["//visibility:public"],
+)
+
+haskell_library(
+    name = "one",
+    srcs = ["One.hs"],
+    deps = [
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_library(
+    name = "two",
+    srcs = ["Two.hs"],
+    deps = [
+        ":one",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_test(
+    name = "two-libs",
+    srcs = ["Main.hs"],
+    coverage_report_format = "html",
+    expected_covered_expressions_percentage = 55,
+    expected_uncovered_expression_count = 4,
+    experimental_coverage_source_patterns = [":two"],
+    strict_coverage_analysis = True,
+    tags = ["coverage-compatible"],
+    deps = [
+        ":two",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/two-libs/Main.hs b/third_party/bazel/rules_haskell/tests/two-libs/Main.hs
new file mode 100644
index 000000000000..99ce449a5281
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/two-libs/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import Two (two)
+
+main :: IO ()
+main = putStrLn ("Two: " ++ show two)
diff --git a/third_party/bazel/rules_haskell/tests/two-libs/One.hs b/third_party/bazel/rules_haskell/tests/two-libs/One.hs
new file mode 100644
index 000000000000..f8f04ab2640c
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/two-libs/One.hs
@@ -0,0 +1,4 @@
+module One (one) where
+
+one :: Int
+one = 1
diff --git a/third_party/bazel/rules_haskell/tests/two-libs/Two.hs b/third_party/bazel/rules_haskell/tests/two-libs/Two.hs
new file mode 100644
index 000000000000..5672263c3d5f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/two-libs/Two.hs
@@ -0,0 +1,14 @@
+module Two (two) where
+
+import One (one)
+
+two :: Int
+two = 
+    if True then
+        one + one
+    else 
+        if True then
+            1
+        else
+            2
+    
\ No newline at end of file
diff --git a/third_party/bazel/rules_haskell/tests/unit-tests/BUILD.bazel b/third_party/bazel/rules_haskell/tests/unit-tests/BUILD.bazel
new file mode 100644
index 000000000000..990035af685f
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/unit-tests/BUILD.bazel
@@ -0,0 +1,125 @@
+load(
+    ":tests.bzl",
+    "create_rpath_entry_test",
+    "dedup_on_test",
+    "parent_dir_path_test",
+)
+
+parent_dir_path_test(
+    name = "parent_dir_just_file",
+    filename = "foo",
+    output = ["."],
+)
+
+parent_dir_path_test(
+    name = "parent_dir",
+    filename = "foo/",
+    output = ["foo"],
+)
+
+parent_dir_path_test(
+    name = "parent_dir_file",
+    filename = "foo/bar",
+    output = ["foo"],
+)
+
+parent_dir_path_test(
+    name = "parent_dir_file_dots",
+    filename = "foo/../bar",
+    output = [
+        "foo",
+        "..",
+    ],
+)
+
+parent_dir_path_test(
+    name = "parent_dir_rooted",
+    filename = "/foo/bar",
+    output = [
+        "",
+        "foo",
+    ],
+)
+
+create_rpath_entry_test(
+    name = "rpath_entry_simple",
+    binary_short_path = "foo/a.so",
+    dependency_short_path = "bar/b.so",
+    output = "../bar",
+)
+
+# checks that a binary in //:bin works properly
+create_rpath_entry_test(
+    name = "rpath_entry_binary_root",
+    binary_short_path = "bin",
+    dependency_short_path = "xyz/b.so",
+    output = "xyz",
+)
+
+# same for dependency
+create_rpath_entry_test(
+    name = "rpath_entry_dep_root",
+    binary_short_path = "lib/bin",
+    dependency_short_path = "b.so",
+    output = "..",
+)
+
+create_rpath_entry_test(
+    name = "rpath_entry_simple_filename",
+    binary_short_path = "foo/a.so",
+    dependency_short_path = "bar/b.so",
+    keep_filename = True,
+    output = "../bar/b.so",
+)
+
+create_rpath_entry_test(
+    name = "rpath_entry_prefix",
+    binary_short_path = "foo/a.so",
+    dependency_short_path = "bar/b.so",
+    output = "$ORIGIN/../bar",
+    prefix = "$ORIGIN",
+)
+
+# if the short-paths have leading dots, they are in `external`
+
+create_rpath_entry_test(
+    name = "rpath_entry_binary_leading_dots_dep",
+    # non-external
+    binary_short_path = "foo/a.so",
+    # external dep
+    dependency_short_path = "../bar/b.so",
+    output = "../external/bar",
+)
+
+create_rpath_entry_test(
+    name = "rpath_entry_binary_leading_dots_bin",
+    # external dep
+    binary_short_path = "../foo/a.so",
+    # non-external
+    dependency_short_path = "bar/b.so",
+    # back through `external`
+    output = "../../bar",
+)
+
+create_rpath_entry_test(
+    name = "rpath_entry_binary_leading_dots_both",
+    # external dep
+    binary_short_path = "../foo/a.so",
+    # external dep
+    dependency_short_path = "../bar/b.so",
+    # stay in `external`
+    output = "../bar",
+)
+
+# we have no idea how to handle internal dots, should they arise
+# create_rpath_entry_test(
+#     name = "rpath_entry_binary_internal_dots",
+#     binary_short_path = "foo/../../a.so",
+#     dependency_short_path = "../bar/../b.so",
+#     # but that doesn’t change anything for the runpath
+#     output = "../bar",
+# )
+
+dedup_on_test(
+    name = "dedup_on_test",
+)
diff --git a/third_party/bazel/rules_haskell/tests/unit-tests/tests.bzl b/third_party/bazel/rules_haskell/tests/unit-tests/tests.bzl
new file mode 100644
index 000000000000..58a3f400adbe
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/unit-tests/tests.bzl
@@ -0,0 +1,83 @@
+load(
+    "@bazel_skylib//lib:unittest.bzl",
+    "asserts",
+    unit = "unittest",
+)
+load("//haskell:private/actions/link.bzl", "create_rpath_entry", "parent_dir_path")
+load("//haskell:private/list.bzl", "list")
+
+def parent_dir_path_test_impl(ctx):
+    env = unit.begin(ctx)
+    asserts.equals(
+        env,
+        expected = ctx.attr.output,
+        actual = parent_dir_path(ctx.attr.filename),
+    )
+    unit.end(env)
+
+parent_dir_path_test = unit.make(
+    parent_dir_path_test_impl,
+    attrs = {
+        "filename": attr.string(),
+        "output": attr.string_list(),
+    },
+)
+
+def create_rpath_entry_test_impl(ctx):
+    env = unit.begin(ctx)
+    asserts.equals(
+        env,
+        expected = ctx.attr.output,
+        actual = create_rpath_entry(
+            struct(
+                short_path = ctx.attr.binary_short_path,
+            ),
+            struct(
+                short_path = ctx.attr.dependency_short_path,
+            ),
+            keep_filename = ctx.attr.keep_filename,
+            prefix = ctx.attr.prefix,
+        ),
+    )
+    unit.end(env)
+
+create_rpath_entry_test = unit.make(
+    create_rpath_entry_test_impl,
+    attrs = {
+        "binary_short_path": attr.string(),
+        "dependency_short_path": attr.string(),
+        "keep_filename": attr.bool(default = False, mandatory = False),
+        "prefix": attr.string(default = "", mandatory = False),
+        "output": attr.string(),
+    },
+)
+
+def compare_x(el):
+    return el.x
+
+def dedup_on_test_impl(ctx):
+    env = unit.begin(ctx)
+    asserts.equals(
+        env,
+        expected = [],
+        actual = list.dedup_on(compare_x, []),
+    )
+    asserts.equals(
+        env,
+        expected = [struct(x = 3)],
+        actual = list.dedup_on(
+            compare_x,
+            [struct(x = 3), struct(x = 3), struct(x = 3)],
+        ),
+    )
+    asserts.equals(
+        env,
+        expected = [struct(x = 3), struct(x = 4), struct(x = 5)],
+        actual = list.dedup_on(
+            compare_x,
+            [struct(x = 3), struct(x = 4), struct(x = 3), struct(x = 5), struct(x = 3)],
+        ),
+    )
+    unit.end(env)
+
+dedup_on_test = unit.make(dedup_on_test_impl)
diff --git a/third_party/bazel/rules_haskell/tests/version-macros/BUILD.bazel b/third_party/bazel/rules_haskell/tests/version-macros/BUILD.bazel
new file mode 100644
index 000000000000..b2be1b7d1598
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/version-macros/BUILD.bazel
@@ -0,0 +1,59 @@
+load(
+    "@io_tweag_rules_haskell//haskell:haskell.bzl",
+    "haskell_library",
+    "haskell_test",
+)
+load(
+    "@io_tweag_rules_haskell//haskell:c2hs.bzl",
+    "c2hs_library",
+)
+
+package(default_testonly = 1)
+
+haskell_library(
+    name = "versioned-lib",
+    srcs = ["VersionedLib.hs"],
+    version = "1.2.3.4",
+    deps = ["//tests/hackage:base"],
+)
+
+haskell_test(
+    name = "version_macros",
+    size = "small",
+    srcs = [
+        "HsLib.hs",
+        "HscLib.hsc",
+        "Main.hs",
+    ],
+    version = "3.4.5.6",
+    deps = [
+        ":versioned-lib",
+        "//tests/hackage:base",
+    ],
+)
+
+c2hs_library(
+    name = "c2hs-lib",
+    srcs = ["C2hsLib.chs"],
+    tags = ["requires_c2hs"],
+    version = "4.5.6.7",
+    deps = [
+        ":versioned-lib",
+        "//tests/hackage:base",
+    ],
+)
+
+haskell_test(
+    name = "version_macros_c2hs",
+    size = "small",
+    srcs = [
+        "MainC2hs.hs",
+        ":c2hs-lib",
+    ],
+    tags = ["requires_c2hs"],
+    version = "4.5.6.7",
+    deps = [
+        ":versioned-lib",
+        "//tests/hackage:base",
+    ],
+)
diff --git a/third_party/bazel/rules_haskell/tests/version-macros/C2hsLib.chs b/third_party/bazel/rules_haskell/tests/version-macros/C2hsLib.chs
new file mode 100644
index 000000000000..4b484af2a3ed
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/version-macros/C2hsLib.chs
@@ -0,0 +1,44 @@
+module C2hsLib where
+
+import Control.Monad (unless)
+
+check_version_versioned_lib :: IO ()
+#ifndef VERSION_versioned_lib
+check_version_versioned_lib = fail "c2hs: VERSION_versioned_lib missing"
+#else
+check_version_versioned_lib =
+  unless ({#const VERSION_versioned_lib#} == "1.2.3.4") $
+    fail "c2hs: VERSION_versioned_lib invalid"
+#endif
+
+check_min_version_versioned_lib :: IO ()
+#ifndef MIN_VERSION_versioned_lib
+check_min_version_versioned_lib = fail "c2hs: MIN_VERSION_versioned_lib missing"
+#elif !MIN_VERSION_versioned_lib(1,2,3)
+check_min_version_versioned_lib = fail "c2hs: MIN_VERSION_versioned_lib invalid"
+#else
+check_min_version_versioned_lib = pure ()
+#endif
+
+check_version_base :: IO ()
+#ifndef VERSION_base
+check_version_base = fail "c2hs: VERSION_base missing"
+#else
+check_version_base = pure ()
+#endif
+
+check_min_version_base :: IO ()
+#ifndef MIN_VERSION_base
+check_min_version_base = fail "c2hs: MIN_VERSION_base missing"
+#elif !MIN_VERSION_base(0,0,0)
+check_min_version_base = fail "c2hs: MIN_VERSION_base invalid"
+#else
+check_min_version_base = pure ()
+#endif
+
+check :: IO ()
+check = do
+  check_version_versioned_lib
+  check_min_version_versioned_lib
+  check_version_base
+  check_min_version_base
diff --git a/third_party/bazel/rules_haskell/tests/version-macros/HsLib.hs b/third_party/bazel/rules_haskell/tests/version-macros/HsLib.hs
new file mode 100644
index 000000000000..ce61906c81e6
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/version-macros/HsLib.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE CPP #-}
+
+module HsLib where
+
+import Control.Monad (unless)
+
+check_version_versioned_lib :: IO ()
+#ifndef VERSION_versioned_lib
+check_version_versioned_lib = fail "hs: VERSION_versioned_lib missing"
+#else
+check_version_versioned_lib =
+  unless (VERSION_versioned_lib == "1.2.3.4") $
+    fail "hs: VERSION_versioned_lib invalid"
+#endif
+
+check_min_version_versioned_lib :: IO ()
+#ifndef MIN_VERSION_versioned_lib
+check_min_version_versioned_lib = fail "hs: MIN_VERSION_versioned_lib missing"
+#elif !MIN_VERSION_versioned_lib(1,2,3)
+check_min_version_versioned_lib = fail "hs: MIN_VERSION_versioned_lib invalid"
+#else
+check_min_version_versioned_lib = pure ()
+#endif
+
+check_version_base :: IO ()
+#ifndef VERSION_base
+check_version_base = fail "hs: VERSION_base missing"
+#else
+check_version_base = pure ()
+#endif
+
+check_min_version_base :: IO ()
+#ifndef MIN_VERSION_base
+check_min_version_base = fail "hs: MIN_VERSION_base missing"
+#elif !MIN_VERSION_base(0,0,0)
+check_min_version_base = fail "hs: MIN_VERSION_base invalid"
+#else
+check_min_version_base = pure ()
+#endif
+
+check :: IO ()
+check = do
+  check_version_versioned_lib
+  check_min_version_versioned_lib
+  check_version_base
+  check_min_version_base
diff --git a/third_party/bazel/rules_haskell/tests/version-macros/HscLib.hsc b/third_party/bazel/rules_haskell/tests/version-macros/HscLib.hsc
new file mode 100644
index 000000000000..387e73a08997
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/version-macros/HscLib.hsc
@@ -0,0 +1,44 @@
+module HscLib where
+
+import Control.Monad (unless)
+
+check_version_versioned_lib :: IO ()
+#ifndef VERSION_versioned_lib
+check_version_versioned_lib = fail "hsc2hs: VERSION_versioned_lib missing"
+#else
+check_version_versioned_lib =
+  unless (#{const_str VERSION_versioned_lib} == "1.2.3.4") $
+    fail "hsc2hs: VERSION_versioned_lib invalid"
+#endif
+
+check_min_version_versioned_lib :: IO ()
+#ifndef MIN_VERSION_versioned_lib
+check_min_version_versioned_lib = fail "hsc2hs: MIN_VERSION_versioned_lib missing"
+#elif !MIN_VERSION_versioned_lib(1,2,3)
+check_min_version_versioned_lib = fail "hsc2hs: MIN_VERSION_versioned_lib invalid"
+#else
+check_min_version_versioned_lib = pure ()
+#endif
+
+check_version_base :: IO ()
+#ifndef VERSION_base
+check_version_base = fail "hsc2hs: VERSION_base missing"
+#else
+check_version_base = pure ()
+#endif
+
+check_min_version_base :: IO ()
+#ifndef MIN_VERSION_base
+check_min_version_base = fail "hsc2hs: MIN_VERSION_base missing"
+#elif !MIN_VERSION_base(0,0,0)
+check_min_version_base = fail "hsc2hs: MIN_VERSION_base invalid"
+#else
+check_min_version_base = pure ()
+#endif
+
+check :: IO ()
+check = do
+  check_version_versioned_lib
+  check_min_version_versioned_lib
+  check_version_base
+  check_min_version_base
diff --git a/third_party/bazel/rules_haskell/tests/version-macros/Main.hs b/third_party/bazel/rules_haskell/tests/version-macros/Main.hs
new file mode 100644
index 000000000000..bbcd108d0b6b
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/version-macros/Main.hs
@@ -0,0 +1,9 @@
+module Main where
+
+import qualified HscLib
+import qualified HsLib
+
+main :: IO ()
+main = do
+  HscLib.check
+  HsLib.check
diff --git a/third_party/bazel/rules_haskell/tests/version-macros/MainC2hs.hs b/third_party/bazel/rules_haskell/tests/version-macros/MainC2hs.hs
new file mode 100644
index 000000000000..82e4ec72e248
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/version-macros/MainC2hs.hs
@@ -0,0 +1,7 @@
+module Main where
+
+import qualified C2hsLib
+
+main :: IO ()
+main = do
+  C2hsLib.check
diff --git a/third_party/bazel/rules_haskell/tests/version-macros/VersionedLib.hs b/third_party/bazel/rules_haskell/tests/version-macros/VersionedLib.hs
new file mode 100644
index 000000000000..0dfd5577e7aa
--- /dev/null
+++ b/third_party/bazel/rules_haskell/tests/version-macros/VersionedLib.hs
@@ -0,0 +1 @@
+module VersionedLib where