diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,8 @@
+# Changelog for linear-tests
+
+## 0.1.1 [2020.02.16]
+- Added `InvertibleM33`
+- Added `BasisV3`
+
+## 0.1 [2020.02.14]
+- Added various Arbitrary instances for `V3`, `V4`, `M33`, `M44`, and `Quaternion`.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Peter Lu (c) 2020
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Author name here nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+[![CircleCI](https://circleci.com/gh/pdlla/linear-tests.svg?style=svg)](https://circleci.com/gh/pdlla/linear-tests)
+
+# linear-tests
+This library provides [QuickCheck](https://hackage.haskell.org/package/QuickCheck) `Arbitrary` instances of data types in the [linear](http://hackage.haskell.org/package/linear) package.
+
+The property tests in this package test both the newly defined `Arbitrary` instances as well as methods in the linear package itself. It's hard to distinguish between the two types of test so they are all just lumped together.
+
+The current instances are just ones I (and hopefully others) find useful. Feel free to add your own and submit a PR. 
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/linear-tests.cabal b/linear-tests.cabal
new file mode 100644
--- /dev/null
+++ b/linear-tests.cabal
@@ -0,0 +1,69 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.31.2.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 7d0fbb63b1f76fdccf74b6a953e18091d9a95568447c823ca6244cc4a413b720
+
+name:           linear-tests
+version:        0.1.1.0
+synopsis:       Linear Algebra
+description:    Please see the README on GitHub at <https://github.com/pdlla/linear-tests#readme>
+category:       Math, Algebra, Testing
+homepage:       https://github.com/pdlla/linear-tests#readme
+bug-reports:    https://github.com/pdlla/linear-tests/issues
+author:         pdlla
+maintainer:     chippermonky@gmail.com
+copyright:      2020 pdlla
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
+
+source-repository head
+  type: git
+  location: https://github.com/pdlla/linear-tests
+
+library
+  exposed-modules:
+      Linear.Arbitrary
+      Linear.Matrix.Arbitrary
+      Linear.Quaternion.Arbitrary
+      Linear.V3.Arbitrary
+      Linear.V4.Arbitrary
+  other-modules:
+      Paths_linear_tests
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      QuickCheck
+    , base >=4.7 && <5
+    , lens
+    , linear
+  default-language: Haskell2010
+
+test-suite linear-tests-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Linear.MatrixSpec
+      Linear.QuaternionSpec
+      Linear.V3Spec
+      Linear.V4Spec
+      Paths_linear_tests
+  hs-source-dirs:
+      test
+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      QuickCheck
+    , base >=4.7 && <5
+    , hspec
+    , hspec-core
+    , lens
+    , linear
+    , linear-tests
+  default-language: Haskell2010
diff --git a/src/Linear/Arbitrary.hs b/src/Linear/Arbitrary.hs
new file mode 100644
--- /dev/null
+++ b/src/Linear/Arbitrary.hs
@@ -0,0 +1,11 @@
+
+
+module Linear.Arbitrary (
+  module Linear.Matrix.Arbitrary,
+  module Linear.Quaternion.Arbitrary,
+  module Linear.V3.Arbitrary
+) where
+
+import           Linear.Matrix.Arbitrary
+import           Linear.Quaternion.Arbitrary
+import           Linear.V3.Arbitrary
diff --git a/src/Linear/Matrix/Arbitrary.hs b/src/Linear/Matrix/Arbitrary.hs
new file mode 100644
--- /dev/null
+++ b/src/Linear/Matrix/Arbitrary.hs
@@ -0,0 +1,66 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Linear.Matrix.Arbitrary (
+  InvertibleM33(..)
+  , DiagM33(..)
+  , InvertibleM44(..)
+  , AffineM44(..)
+  , InvertibleAffineM44(..)
+) where
+
+import           Control.Lens
+
+import           Linear.Epsilon
+import           Linear.Matrix
+import           Linear.Metric
+import qualified Linear.V3           as V3
+import qualified Linear.V4           as V4
+import           Test.QuickCheck
+
+import           Linear.V3.Arbitrary ()
+import           Linear.V4.Arbitrary ()
+
+
+-- | `Arbitrary InvertibleM33` instances are always invertible
+newtype InvertibleM33 a = InvertibleM33 { unInvertibleM33 :: M33 a }  deriving (Show)
+
+instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (InvertibleM33 a) where
+  arbitrary = fmap InvertibleM33 $ (V3.V3 <$> arbitrary <*> arbitrary <*> arbitrary) `suchThat` (not . nearZero . det33)
+
+-- | `Arbitrary DiagM33` instances only have non-zero diagonal entries (could still be zero)
+newtype DiagM33 a = DiagM33 { unDiagM33 :: M33 a } deriving (Show)
+
+instance (Arbitrary a, Num a) => Arbitrary (DiagM33 a) where
+  arbitrary = do
+    s1 <- arbitrary
+    s2 <- arbitrary
+    s3 <- arbitrary
+    return . DiagM33 $ V3.V3
+      (V3.V3 s1 0 0)
+      (V3.V3 0 s2 0)
+      (V3.V3 0 0 s3)
+
+newtype InvertibleM44 a = InvertibleM44 { unInvertibleM44 :: M44 a } deriving (Show)
+
+instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (InvertibleM44 a) where
+  arbitrary = fmap InvertibleM44 $ (V4.V4 <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary) `suchThat` (not . nearZero . det44)
+
+-- | `Arbitrary AffineM44` instances are affine M44 matrices (i.e. have [0,0,0,1] in last row)
+newtype AffineM44 a = AffineM44 { unAffineM44 :: M44 a } deriving (Show)
+
+instance (Arbitrary a, Num a) => Arbitrary (AffineM44 a) where
+  arbitrary = do
+    r1 <- arbitrary
+    r2 <- arbitrary
+    r3 <- arbitrary
+    return . AffineM44 $ V4.V4 r1 r2 r3 (V4.V4 0 0 0 1)
+
+
+-- | `Arbitrary InvertibleAffineM44` instances are invertible affine M44 matrices
+newtype InvertibleAffineM44 a = InvertibleAffineM44 { unInvertibleAffineM44 :: M44 a } deriving (Show)
+
+instance (Arbitrary a, Num a) => Arbitrary (InvertibleAffineM44 a) where
+  arbitrary = do
+    m33part <- m33_to_m44 <$> arbitrary
+    trans <- arbitrary
+    return . InvertibleAffineM44 $ set (V4._w . V4._w) 1 . set translation trans $ m33part
diff --git a/src/Linear/Quaternion/Arbitrary.hs b/src/Linear/Quaternion/Arbitrary.hs
new file mode 100644
--- /dev/null
+++ b/src/Linear/Quaternion/Arbitrary.hs
@@ -0,0 +1,28 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Linear.Quaternion.Arbitrary (
+  CartesianQuaternion(..)
+) where
+
+import           Linear.Epsilon
+import           Linear.Matrix       ()
+import           Linear.Quaternion
+import           Linear.V3.Arbitrary
+
+import           Test.QuickCheck
+
+-- | `Arbitrary Quaternion` instance
+instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (Quaternion a) where
+  arbitrary = do
+    UnitV3 v <- arbitrary
+    r <- arbitrary
+    return $ axisAngle v r
+
+-- | Arbitrary instances of this type are restricted to increment of 90 degrees along cartesian axis
+newtype CartesianQuaternion a = CartesianQuaternion { unCartesianQuaternion :: Quaternion a } deriving (Show)
+
+instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (CartesianQuaternion a) where
+  arbitrary = do
+    CartesianUnitV3 v <- arbitrary
+    r <- elements [0, pi/2, pi, 3*pi/2]
+    return . CartesianQuaternion $ axisAngle v r
diff --git a/src/Linear/V3/Arbitrary.hs b/src/Linear/V3/Arbitrary.hs
new file mode 100644
--- /dev/null
+++ b/src/Linear/V3/Arbitrary.hs
@@ -0,0 +1,48 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Linear.V3.Arbitrary (
+  UnitV3(..)
+  , CartesianUnitV3(..)
+  , BasisV3(..)
+) where
+
+import           Control.Lens      hiding (elements)
+
+import           Linear.Conjugate
+import           Linear.Epsilon
+import           Linear.Metric
+import qualified Linear.Quaternion as Q
+import           Linear.V3
+import           Linear.Vector
+
+import           Test.QuickCheck
+
+-- | `Arbitrary V3` has no restrictions on components
+instance (Arbitrary a) => Arbitrary (V3 a) where
+  arbitrary = V3 <$> arbitrary <*> arbitrary <*> arbitrary
+
+-- | `Arbitrary UnitV3` always has norm 1
+newtype UnitV3 a = UnitV3 {unUnitV3 :: V3 a}  deriving (Show)
+
+instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (UnitV3 a) where
+  arbitrary = do
+    v <- V3 <$> arbitrary <*> arbitrary <*> (arbitrary `suchThat` (not . nearZero))
+    return . UnitV3 . signorm $ v
+
+-- | `Arbitrary CartesianUnitV3` is a unit vector along cartesian axis
+newtype CartesianUnitV3 a = CartesianUnitV3 {unCartesianUnitV3 :: V3 a}  deriving (Show)
+
+instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (CartesianUnitV3 a) where
+  arbitrary = elements $ CartesianUnitV3 <$> [unit _x, unit _y, unit _z, - (unit _x), - (unit _y), - (unit _z)]
+
+-- | `Aribtrary BasisV3` is a orthonormal set of vectors
+newtype BasisV3 a = BasisV3 { unBasisV3 :: (V3 a, V3 a, V3 a) } deriving (Show)
+
+instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (BasisV3 a) where
+  arbitrary = do
+    UnitV3 x <- arbitrary
+    o <- arbitrary `suchThat` (not . nearZero . dot x)
+    let
+      y = signorm $ cross x o
+      z = cross x y
+    return $ BasisV3 (x, y, z)
diff --git a/src/Linear/V4/Arbitrary.hs b/src/Linear/V4/Arbitrary.hs
new file mode 100644
--- /dev/null
+++ b/src/Linear/V4/Arbitrary.hs
@@ -0,0 +1,23 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Linear.V4.Arbitrary (
+  UnitV4(..)
+) where
+
+import           Linear.Epsilon
+import           Linear.Metric
+import           Linear.V4
+
+import           Test.QuickCheck
+
+-- | `Arbitrary V4` has no restrictions on components
+instance (Arbitrary a) => Arbitrary (V4 a) where
+  arbitrary = V4 <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
+
+-- | `Arbitrary UnitV4` always has norm 1
+newtype UnitV4 a = UnitV4 {unUnitV4 :: V4 a}  deriving (Show)
+
+instance (Arbitrary a, Epsilon a, Floating a) => Arbitrary (UnitV4 a) where
+  arbitrary = do
+    v <- V4 <$> arbitrary <*> arbitrary <*> arbitrary <*> (arbitrary `suchThat` (not . nearZero))
+    return . UnitV4 . signorm $ v
diff --git a/test/Linear/MatrixSpec.hs b/test/Linear/MatrixSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Linear/MatrixSpec.hs
@@ -0,0 +1,92 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE ExplicitForAll      #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
+
+
+module Linear.MatrixSpec (
+  spec
+) where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Control.Lens
+
+import           Linear.Epsilon
+import qualified Linear.Matrix           as M
+import qualified Linear.V3               as V3
+import qualified Linear.V4               as V4
+
+import           Linear.Matrix.Arbitrary
+
+
+-- | test the property `m * m^-1 == identity`
+prop_Matrix_InvertibleM33_isInvertible :: (Epsilon a, Floating a) => InvertibleM33 a -> Bool
+prop_Matrix_InvertibleM33_isInvertible (InvertibleM33 m) = nearZero ((m M.!*! M.inv33 m ) - M.identity)
+
+isDiagonal :: (Epsilon a) => M.M33 a -> Bool
+isDiagonal m33 = all nearZero $ map (`view` m33) [
+  V3._x . V3._y
+  , V3._x . V3._z
+  , V3._y . V3._x
+  , V3._y . V3._z
+  , V3._z . V3._x
+  , V3._z . V3._y]
+
+-- | test the property that all off diagonal entries are zero
+prop_Matrix_DiagM33_isDiagonal :: (Epsilon a, Floating a) => DiagM33 a -> Bool
+prop_Matrix_DiagM33_isDiagonal = isDiagonal . unDiagM33
+
+isAffine :: (Epsilon a, Floating a) => M.M44 a -> Bool
+isAffine m44 = nearZero $ view V4._w m44 - V4.V4 0 0 0 1
+
+-- | test the property `m * m^-1 == identity`
+prop_Matrix_InvertibleM44_isInvertible :: (Epsilon a, Floating a) => InvertibleM44 a -> Bool
+prop_Matrix_InvertibleM44_isInvertible (InvertibleM44 m) = nearZero ((m M.!*! M.inv44 m ) - M.identity)
+
+-- | test the property that last row is [0,0,0,1]
+prop_Matrix_AffineM44_isAffine :: (Epsilon a, Floating a) => AffineM44 a -> Bool
+prop_Matrix_AffineM44_isAffine (AffineM44 m44) = isAffine m44
+
+-- | test that it is closed under multiplication
+prop_Matrix_AffineM44_isClosedUnderMultiplication :: (Epsilon a, Floating a) => NonEmptyList (AffineM44 a) -> Bool
+prop_Matrix_AffineM44_isClosedUnderMultiplication = isAffine . foldr1 (M.!*!) . map unAffineM44 . getNonEmpty
+
+-- | test the property that last row is [0,0,0,1]
+prop_Matrix_InvertibleAffineM44_isAffine :: (Epsilon a, Floating a) => InvertibleAffineM44 a -> Bool
+prop_Matrix_InvertibleAffineM44_isAffine (InvertibleAffineM44 m44) = isAffine m44
+
+-- | test the property `m * m^-1 == identity`
+prop_Matrix_InvertibleAffineM44_isInvertible :: (Epsilon a, Floating a) => InvertibleAffineM44 a -> Bool
+prop_Matrix_InvertibleAffineM44_isInvertible (InvertibleAffineM44 m44) = isAffine m44
+
+spec :: Spec
+spec = specTyped @Double
+
+specTyped :: forall a. (Eq a, Show a, Arbitrary a, Epsilon a, Floating a) => Spec
+specTyped = do
+  describe "Matrix" $ do
+    describe "M33" $ do
+      describe "Arbitrary" $ do
+        describe "InvertibleM33" $ do
+          it "satifies property `m * m^-1 == identity`" $
+            property $ prop_Matrix_InvertibleM33_isInvertible @a
+        describe "DiagM33" $ do
+          it "satisfies property that all off diagonal entries are zero" $
+            property $ prop_Matrix_DiagM33_isDiagonal @a
+    describe "M44" $ do
+      describe "Arbitrary" $ do
+        describe "InvertibleM44" $ do
+          it "satifies property `m * m^-1 == identity`" $
+            property $ prop_Matrix_InvertibleM44_isInvertible @a
+        describe "AffineM44" $ do
+          it "satisfies property [0,0,0,1] in last row" $
+            property $ prop_Matrix_AffineM44_isAffine @a
+          it "is closed under multiplication" $
+            property $ prop_Matrix_AffineM44_isClosedUnderMultiplication @a
+        describe "InvertibleAffineM44" $ do
+          it "satisfies property [0,0,0,1] in last row" $
+            property $ prop_Matrix_InvertibleAffineM44_isAffine @a
+          it "satifies `m * m^-1 == identity`" $
+            property $ prop_Matrix_InvertibleAffineM44_isInvertible @a
diff --git a/test/Linear/QuaternionSpec.hs b/test/Linear/QuaternionSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Linear/QuaternionSpec.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE ExplicitForAll      #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
+
+
+module Linear.QuaternionSpec (
+  spec
+) where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Linear.Conjugate
+import           Linear.Epsilon
+import           Linear.Metric
+import qualified Linear.Quaternion           as Q
+import qualified Linear.V3                   as V
+
+import           Linear.Quaternion.Arbitrary
+import           Linear.V3.Arbitrary
+
+-- | tests for the property `|(q*v)·v| = 1 or 0`
+prop_CartesianQuaternion_arbitrary :: (Show a, Epsilon a, RealFloat a, Conjugate a) => CartesianQuaternion a -> CartesianUnitV3 a -> Bool
+prop_CartesianQuaternion_arbitrary (CartesianQuaternion q) (CartesianUnitV3 v) = nearZero qvv || nearZero (qvv - 1) where
+  qvv = abs $ Q.rotate q v `dot` v
+
+-- | tests for the property `|q| = 1`
+prop_Quaternion_arbitrary :: (Epsilon a, RealFloat a, Conjugate a) => Q.Quaternion a -> Bool
+prop_Quaternion_arbitrary q = nearZero $ norm q - 1
+
+-- | tests for the property `q * conjugate(q) = identity`
+prop_Quaternion_conjugate :: (Epsilon a, RealFloat a, Conjugate a) => Q.Quaternion a -> Bool
+prop_Quaternion_conjugate q = nearZero $ distance (q * conjugate q) (Q.Quaternion 1 (V.V3 0 0 0))
+
+identityQuaternion :: (RealFloat a) => Q.Quaternion a
+identityQuaternion = 1 -- Quaternion 1 (V3 0 0 0)
+
+spec :: Spec
+spec = specTyped @Double
+
+specTyped :: forall a. (Eq a, Show a, Arbitrary a, Epsilon a, RealFloat a, Conjugate a) => Spec
+specTyped = do
+  describe "Quaternion" $ do
+    describe "Arbitrary" $ do
+      describe "Quaternion" $ do
+        it "satisfies the property `|q| = 1`" $
+          property $ prop_Quaternion_arbitrary @a
+      describe "CartesianQuaternion" $ do
+        it "satisfies the property `|(q*v)·v| = 1 or 0`" $
+          property $ prop_CartesianQuaternion_arbitrary @a
+    describe "Conjugate" $ do
+      it "satisfies the property `q * conjugate(q) = identity`" $
+        property $ prop_Quaternion_conjugate @a
diff --git a/test/Linear/V3Spec.hs b/test/Linear/V3Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Linear/V3Spec.hs
@@ -0,0 +1,71 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE ExplicitForAll      #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
+
+
+module Linear.V3Spec (
+  spec
+) where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Linear.Epsilon
+import           Linear.Metric
+import           Linear.Vector
+
+import           Linear.V3.Arbitrary
+
+-- | test for the property `|v| = 1`
+prop_UnitV3_isUnit :: (Epsilon a, Floating a) => UnitV3 a -> Bool
+prop_UnitV3_isUnit (UnitV3 v) = nearZero $ norm v - 1
+
+-- | test for the property `|v| = 1`
+prop_CartesianUnitV3_isUnit :: (Epsilon a, Floating a) => CartesianUnitV3 a -> Bool
+prop_CartesianUnitV3_isUnit (CartesianUnitV3 v) = nearZero $ norm v - 1
+
+-- | test for the property `|u·v| = 1 or 0`
+prop_CartesianUnitV3_isOrthogonal :: (Epsilon a, Floating a) => CartesianUnitV3 a -> CartesianUnitV3 a -> Bool
+prop_CartesianUnitV3_isOrthogonal (CartesianUnitV3 u) (CartesianUnitV3 v) = nearZero uv || nearZero (uv - 1) where
+  uv = abs $ u `dot` v
+
+-- | test for the property that all vectors are orthogonal and normal
+prop_BasisV3_isOrthonormal  :: (Epsilon a, Floating a) => BasisV3 a -> Bool
+prop_BasisV3_isOrthonormal (BasisV3 (x,y,z)) =
+  nearZero (dot x y) && nearZero (dot y z) && nearZero (dot z x)
+    && nearZero (norm x - 1) && nearZero (norm y - 1) && nearZero (norm z - 1)
+
+-- | test for the property `|v*c| = c` for unit vector v and arbitrary scalar c
+prop_Metric_V3_norm :: (Epsilon a, Floating a) => UnitV3 a -> a -> Bool
+prop_Metric_V3_norm (UnitV3 v) c = nearZero $ norm (v ^* c) - (abs c)
+
+-- | test for the property `signorm(v*c) = v` for unit vector v and c >= 0
+prop_Metric_V3_signorm :: (Epsilon a, Floating a) => UnitV3 a -> NonZero a -> Bool
+prop_Metric_V3_signorm (UnitV3 v) c = nearZero $ signorm (v ^* (abs (getNonZero c))) - v
+
+
+
+spec :: Spec
+spec = specTyped @Double
+
+specTyped :: forall a. (Eq a, Show a, Arbitrary a, Epsilon a, Floating a) => Spec
+specTyped = do
+  describe "V3" $ do
+    describe "Arbitrary" $ do
+      describe "UnitV3" $ do
+        it "satisfies the property `|v| = 1`" $ do
+          property $ prop_UnitV3_isUnit @a
+      describe "CartesianUnitV3" $ do
+        it "satisfies the property `|v| = 1`" $ do
+          property $ prop_CartesianUnitV3_isUnit @a
+        it "satisfies the property `|u·v| = 1 or 0`" $ do
+          property $ prop_CartesianUnitV3_isOrthogonal @a
+      describe "BasisV3" $ do
+        it "satisfies the property that vectors are orthonormal" $ do
+          property $ prop_BasisV3_isOrthonormal @a
+    describe "Metric" $ do
+      it "satisfies property `|v*c| = c` for unit vectors" $
+        property $ prop_Metric_V3_norm @a
+      it "satisfies property `signorm(v*c) = v` for unit vectors" $
+        property $ prop_Metric_V3_signorm @a
diff --git a/test/Linear/V4Spec.hs b/test/Linear/V4Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Linear/V4Spec.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE ExplicitForAll      #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
+
+
+module Linear.V4Spec (
+  spec
+) where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Linear.Epsilon
+import           Linear.Metric
+
+import           Linear.V4.Arbitrary
+
+
+-- | test for the property `|v| = 1`
+prop_UnitV4_isUnit :: (Epsilon a, Floating a) => UnitV4 a -> Bool
+prop_UnitV4_isUnit (UnitV4 v) = nearZero $ norm v - 1
+
+spec :: Spec
+spec = specTyped @Double
+
+specTyped :: forall a. (Eq a, Show a, Arbitrary a, Epsilon a, Floating a) => Spec
+specTyped = do
+  describe "V4" $ do
+    describe "Arbitrary" $ do
+      describe "UnitV4" $ do
+        it "satisfies the property `|v| = 1`" $ do
+          property $ prop_UnitV4_isUnit @a
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+-- hspec auto-discovery stuff
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
