diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,7 @@
+# 0.1.0.1
+
+* Relax required `cabal-version` to 1.10.
+
 # 0.1
 
 * Initial release, matching `Data.Array.Byte` in `base-4.17.0.0` and GHC 9.4.
diff --git a/data-array-byte.cabal b/data-array-byte.cabal
--- a/data-array-byte.cabal
+++ b/data-array-byte.cabal
@@ -1,6 +1,6 @@
-cabal-version:      2.0
+cabal-version:      >=1.10
 name:               data-array-byte
-version:            0.1
+version:            0.1.0.1
 license:            BSD3
 license-file:       LICENSE
 copyright:          (c) Roman Leshchinskiy 2009-2012
@@ -42,3 +42,17 @@
 
     if impl(ghc <9.4)
         exposed-modules: Data.Array.Byte
+
+test-suite data-array-byte-tests
+    type:             exitcode-stdio-1.0
+    main-is:          Main.hs
+    hs-source-dirs:   test
+    default-language: Haskell2010
+    ghc-options:      -Wall
+    build-depends:
+        base,
+        data-array-byte,
+        quickcheck-classes-base >=0.6 && <0.7,
+        tasty >=1.4 && <1.5,
+        tasty-quickcheck >=0.10 && <0.11,
+        template-haskell
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,91 @@
+-- Derived from @primitive@ package.
+
+{-# LANGUAGE TemplateHaskell #-}
+
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+import Control.Monad (when, unless)
+import Data.Array.Byte (ByteArray)
+import Data.Function (on)
+import Data.Proxy (Proxy(..))
+import Data.Semigroup (Semigroup(..), stimesMonoid)
+import Data.Word (Word8)
+import GHC.Exts (IsList(..))
+import Language.Haskell.TH.Syntax (lift)
+import Test.QuickCheck.Classes.Base (eqLaws, ordLaws, monoidLaws, showLaws, isListLaws, semigroupLaws, semigroupMonoidLaws, Laws(..))
+import Test.Tasty (defaultMain, testGroup, TestTree)
+import Test.Tasty.QuickCheck (testProperty, Arbitrary(..), (===), Property, NonNegative(..), property)
+
+main :: IO ()
+main = do
+  testByteArray
+  defaultMain $ testGroup "ByteArray"
+    [ testProperty "equality" byteArrayEqProp
+    , testProperty "reflexivity" byteArrayReflexivityProp
+    , testProperty "compare" byteArrayCompareProp
+    , lawsToTest (eqLaws (Proxy :: Proxy ByteArray))
+    , lawsToTest (ordLaws (Proxy :: Proxy ByteArray))
+    , lawsToTest (monoidLaws (Proxy :: Proxy ByteArray))
+    , lawsToTest (showLaws (Proxy :: Proxy ByteArray))
+    , lawsToTest (isListLaws (Proxy :: Proxy ByteArray))
+    , lawsToTest (semigroupLaws (Proxy :: Proxy ByteArray))
+    , lawsToTest (semigroupMonoidLaws (Proxy :: Proxy ByteArray))
+    , testProperty "stimes" byteArrayStimesProp
+    , testProperty "lift" byteArrayLiftProp
+    ]
+
+byteArrayCompareProp :: [Word8] -> [Word8] -> Property
+byteArrayCompareProp xs ys =
+  compareLengthFirst xs ys === compare (mkByteArray xs) (mkByteArray ys)
+
+byteArrayEqProp :: [Word8] -> [Word8] -> Property
+byteArrayEqProp xs ys =
+  (compareLengthFirst xs ys == EQ) === (mkByteArray xs == mkByteArray ys)
+
+byteArrayReflexivityProp :: [Word8] -> Property
+byteArrayReflexivityProp xs = property $
+  let ba = mkByteArray xs in ba == ba && compare ba ba == EQ
+
+compareLengthFirst :: [Word8] -> [Word8] -> Ordering
+compareLengthFirst xs ys = (compare `on` length) xs ys <> compare xs ys
+
+byteArrayStimesProp :: NonNegative Int -> ByteArray -> Property
+byteArrayStimesProp (NonNegative n) xs = stimes n xs === stimesMonoid n xs
+
+byteArrayLiftProp :: Property
+byteArrayLiftProp =
+  let ba = mkByteArray [0,1,2,3,4,5] in ba === $(lift (fromList [0,1,2,3,4,5] :: ByteArray))
+
+lawsToTest :: Laws -> TestTree
+lawsToTest (Laws name pairs) = testGroup name (map (uncurry testProperty) pairs)
+
+testByteArray :: IO ()
+testByteArray = do
+    let arr1 = mkByteArray ([0xde, 0xad, 0xbe, 0xef] :: [Word8])
+        arr2 = mkByteArray ([0xde, 0xad, 0xbe, 0xef] :: [Word8])
+        arr3 = mkByteArray ([0xde, 0xad, 0xbe, 0xee] :: [Word8])
+        arr4 = mkByteArray ([0xde, 0xad, 0xbe, 0xdd] :: [Word8])
+        arr5 = mkByteArray ([0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xdd] :: [Word8])
+        arr6 = mkByteArray ([0xde, 0xad, 0x00, 0x01, 0xb0] :: [Word8])
+    when (show arr1 /= "[0xde, 0xad, 0xbe, 0xef]") $
+        fail $ "ByteArray Show incorrect: " ++ show arr1
+    when (show arr6 /= "[0xde, 0xad, 0x00, 0x01, 0xb0]") $
+        fail $ "ByteArray Show incorrect: " ++ show arr6
+    unless (arr1 > arr3) $
+        fail $ "ByteArray Ord incorrect"
+    unless (arr1 == arr2) $
+        fail $ "ByteArray Eq incorrect"
+    unless (mappend arr1 arr4 == arr5) $
+        fail $ "ByteArray Monoid mappend incorrect"
+    unless (mappend arr1 (mappend arr3 arr4) == mappend (mappend arr1 arr3) arr4) $
+        fail $ "ByteArray Monoid mappend not associative"
+    unless (mconcat [arr1, arr2, arr3, arr4, arr5] == (arr1 <> arr2 <> arr3 <> arr4 <> arr5)) $
+        fail $ "ByteArray Monoid mconcat incorrect"
+    unless (stimes (3 :: Int) arr4 == (arr4 <> arr4 <> arr4)) $
+        fail $ "ByteArray Semigroup stimes incorrect"
+
+instance Arbitrary ByteArray where
+  arbitrary = mkByteArray <$> arbitrary
+
+mkByteArray :: [Word8] -> ByteArray
+mkByteArray = fromList
