Skip to content

Commit 3725ca2

Browse files
Merge branch 'develop-core-tests-coverage'. Close #502.
**Description** The test coverage of `copilot-core` is currently reported at 25% by Hackage. This is very low. Although there are exceptions to what we can test (record fields of existential types) or should tests (proxies, automatically generated constructors and accessor functions), the coverage of our tests should be higher and anything that is not tested should be documented. **Type** - Maintenance: Increase coverage of tests. **Additional context** None. **Requester** - Ivan Perez **Method to check presence of bug** Not applicable (not a bug). **Expected result** All top-level functions that are not automatically generated by the compiler are tested, except where testing them is not possible or for code that is automatically generated (constructors, record accessor functions). Anything not tested is documented. **Solution implemented** Add tests for all definitions in `Copilot.Core.Type` and `Copilot.Core.Type.Array`, except record accessor functions. Running tests with coverage enabled achieves coverage of 97% for top-level definitions (37 out of 38 definitions). The one not tested is uTypeType, which cannot be tested due to being part of an existential type (it's necessity is being discussed in issue 484). The modules Copilot.Core.Operators and Copilot.Core.Spec and Copilot.Core.Expr only contain datatype definitions, so they do not need to be tested. **Further notes** None.
2 parents fbe9d59 + 16c76a4 commit 3725ca2

File tree

3 files changed

+262
-5
lines changed

3 files changed

+262
-5
lines changed

copilot-core/CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
2024-03-07
22
* Remove deprecated functions in Copilot.Core.Type and
33
Copilot.Core.Type.Array. (#500)
4+
* Increase test coverage. (#502)
45

56
2024-01-07
67
* Version bump (3.18.1). (#493)

copilot-core/tests/Test/Copilot/Core/Type.hs

Lines changed: 242 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1+
{-# LANGUAGE DataKinds #-}
12
-- | Test copilot-core:Copilot.Core.Type.
23
module Test.Copilot.Core.Type where
34

45
-- External imports
6+
import Data.Int (Int16, Int32, Int64, Int8)
7+
import Data.Maybe (isJust)
8+
import Data.Type.Equality (testEquality)
9+
import Data.Word (Word16, Word32, Word64, Word8)
510
import Test.Framework (Test, testGroup)
611
import Test.Framework.Providers.QuickCheck2 (testProperty)
7-
import Test.QuickCheck (Gen, Property, elements,
8-
forAllBlind, shuffle, (==>))
12+
import Test.QuickCheck (Gen, Property, arbitrary, elements,
13+
expectFailure, forAll, forAllBlind,
14+
property, shuffle, (==>))
915

1016
-- Internal imports: library modules being tested
11-
import Copilot.Core.Type (SimpleType (..), Type(..), simpleType)
17+
import Copilot.Core.Type (Field (..), SimpleType (..), Struct (..),
18+
Type (..), Typed, UType (..), Value (..),
19+
accessorName, fieldName, simpleType, typeLength,
20+
typeOf, typeSize)
21+
import Copilot.Core.Type.Array (Array)
1222

1323
-- | All unit tests for copilot-core:Copilot.Core.Type.
1424
tests :: Test.Framework.Test
@@ -24,6 +34,38 @@ tests =
2434
testSimpleTypesEqualityTransitive
2535
, testProperty "uniqueness of equality of simple types"
2636
testSimpleTypesEqualityUniqueness
37+
, testProperty "typeLength matches array size for 1-dimensional arrays"
38+
testTypeLength1
39+
, testProperty "typeLength matches array size for 2-dimensional arrays"
40+
testTypeLength2
41+
, testProperty "typeSize matches full array size for 1-dimensional arrays"
42+
testTypeSize1
43+
, testProperty "typeSize matches full array size for 2-dimensional arrays"
44+
testTypeSize2
45+
, testProperty "equality of types"
46+
testUTypesEqualitySymmetric
47+
, testProperty "equality of utype"
48+
testUTypesEq
49+
, testProperty "inequality of utype"
50+
testUTypesInequality
51+
, testProperty "inequality of utype via typeOf"
52+
testUTypesTypeOfInequality
53+
, testProperty "fieldName matches field name (positive)"
54+
testFieldNameOk
55+
, testProperty "fieldName matches field name (negative)"
56+
testFieldNameFail
57+
, testProperty "Show field name"
58+
testShowField
59+
, testProperty "Show struct"
60+
testShowStruct
61+
, testProperty "accessorName matches field name (positive)"
62+
testAccessorNameOk
63+
, testProperty "accessorName matches field name (negative)"
64+
testAccessorNameFail
65+
, testProperty "typeName matches struct type name (positive)"
66+
testTypeNameOk
67+
, testProperty "typeName matches struct type name (negative)"
68+
testTypeNameFail
2769
]
2870

2971
-- | Test that the function simpleTypes preserves inequality, that is, it
@@ -53,6 +95,8 @@ testSimpleTypesInequality = forAllBlind twoDiffTypes $ \(t1, t2) ->
5395
, simpleType Word64
5496
, simpleType Float
5597
, simpleType Double
98+
, simpleType (Array Int8 :: Type (Array 3 Int8))
99+
, simpleType (Struct (S (Field 0)))
56100
]
57101

58102
-- | Test that the equality relation for simple types is reflexive.
@@ -97,4 +141,199 @@ simpleTypes =
97141
, SFloat
98142
, SDouble
99143
, SStruct
144+
, SArray Int8
145+
, SArray Int16
100146
]
147+
148+
-- | Test that the length of an array is as expected.
149+
testTypeLength1 :: Property
150+
testTypeLength1 = property $ typeLength a == 3
151+
where
152+
a :: Type (Array 3 Int8)
153+
a = Array Int8
154+
155+
-- | Test that the length of an multi-dimensional array is as expected.
156+
testTypeLength2 :: Property
157+
testTypeLength2 = property $ typeLength a == 3
158+
where
159+
a :: Type (Array 3 (Array 12 Int8))
160+
a = Array (Array Int8)
161+
162+
-- | Test that the size of an array is as expected.
163+
testTypeSize1 :: Property
164+
testTypeSize1 = property $ typeLength a == 3
165+
where
166+
a :: Type (Array 3 Int8)
167+
a = Array Int8
168+
169+
-- | Test that the size of a multi-dimensional array is as expected (flattens
170+
-- the array).
171+
testTypeSize2 :: Property
172+
testTypeSize2 = property $ typeSize a == 36
173+
where
174+
a :: Type (Array 3 (Array 12 Int8))
175+
a = Array (Array Int8)
176+
177+
-- | Test that equality is symmetric for UTypes via testEquality.
178+
testUTypesEqualitySymmetric :: Property
179+
testUTypesEqualitySymmetric =
180+
forAllBlind (elements utypes) $ \(UType t1) -> isJust (testEquality t1 t1)
181+
182+
-- | Test that testEquality implies equality for UTypes.
183+
testUTypesEq :: Property
184+
testUTypesEq =
185+
forAllBlind (elements utypes) $ \t@(UType t1) -> isJust (testEquality t1 t1) ==> t == t
186+
187+
-- | Test that any two different UTypes are not equal.
188+
--
189+
-- This function pre-selects two UTypes from a list of different UTypes, which
190+
-- guarantees that they will be different.
191+
testUTypesInequality :: Property
192+
testUTypesInequality = forAllBlind twoDiffTypes $ \(t1, t2) ->
193+
t1 /= t2
194+
where
195+
twoDiffTypes :: Gen (UType, UType)
196+
twoDiffTypes = do
197+
shuffled <- shuffle utypes
198+
case shuffled of
199+
(t1:t2:_) -> return (t1, t2)
200+
_ -> return (UType Bool, UType Bool)
201+
202+
-- | Different UTypes.
203+
utypes :: [UType]
204+
utypes =
205+
[ UType Bool
206+
, UType Int8
207+
, UType Int16
208+
, UType Int32
209+
, UType Int64
210+
, UType Word8
211+
, UType Word16
212+
, UType Word32
213+
, UType Word64
214+
, UType Float
215+
, UType Double
216+
, UType a
217+
, UType b
218+
, UType c
219+
]
220+
where
221+
a :: Type (Array 3 Int8)
222+
a = Array Int8
223+
224+
b :: Type (Array 4 Int8)
225+
b = Array Int8
226+
227+
c :: Type S
228+
c = Struct (S (Field 0))
229+
230+
-- | Test that any two different UTypes are not equal.
231+
--
232+
-- This function pre-selects two UTypes from a list of different UTypes built
233+
-- via the function typeOf, which guarantees that they will be different.
234+
testUTypesTypeOfInequality :: Property
235+
testUTypesTypeOfInequality = forAllBlind twoDiffTypes $ \(t1@(UType t1'), t2@(UType t2')) ->
236+
-- The seqs are important: otherwise, the coverage goes down drastically
237+
-- because the typeOf function is not really executed.
238+
t1' `seq` t2' `seq` t1 /= t2
239+
where
240+
twoDiffTypes :: Gen (UType, UType)
241+
twoDiffTypes = do
242+
shuffled <- shuffle uTypesTypeOf
243+
case shuffled of
244+
(t1:t2:_) -> t1 `seq` t2 `seq` return (t1, t2)
245+
_ -> return (UType Bool, UType Bool)
246+
247+
-- | Different UTypes, produced by using the function typeOf.
248+
uTypesTypeOf :: [UType]
249+
uTypesTypeOf =
250+
[ UType (typeOf :: Type Bool)
251+
, UType (typeOf :: Type Int8)
252+
, UType (typeOf :: Type Int16)
253+
, UType (typeOf :: Type Int32)
254+
, UType (typeOf :: Type Int64)
255+
, UType (typeOf :: Type Word8)
256+
, UType (typeOf :: Type Word16)
257+
, UType (typeOf :: Type Word32)
258+
, UType (typeOf :: Type Word64)
259+
, UType (typeOf :: Type Float)
260+
, UType (typeOf :: Type Double)
261+
, UType (typeOf :: Type (Array 3 Int8))
262+
, UType (typeOf :: Type S)
263+
]
264+
265+
-- | Test the fieldName function (should succeed).
266+
testFieldNameOk :: Property
267+
testFieldNameOk = forAll arbitrary $ \k ->
268+
fieldName (s1 (S (Field k))) == s1FieldName
269+
where
270+
s1FieldName = "field"
271+
272+
-- | Test the fieldName function (should fail).
273+
testFieldNameFail :: Property
274+
testFieldNameFail = expectFailure $ property $
275+
fieldName (s1 sampleS) == s1FieldName
276+
where
277+
sampleS = S (Field 0)
278+
s1FieldName = "Field"
279+
280+
-- | Test showing a field of a struct.
281+
testShowField :: Property
282+
testShowField = forAll arbitrary $ \k ->
283+
show (s1 (S (Field k))) == ("field:" ++ show k)
284+
285+
-- | Test showing a struct.
286+
testShowStruct :: Property
287+
testShowStruct = forAll arbitrary $ \k ->
288+
show (S (Field k)) == "<field:" ++ show k ++ ">"
289+
290+
-- | Test the accessorName of a field of a struct (should succeed).
291+
testAccessorNameOk :: Property
292+
testAccessorNameOk = property $
293+
accessorName s1 == s1FieldName
294+
where
295+
s1FieldName = "field"
296+
297+
-- | Test the accessorName of a field of a struct (should fail).
298+
testAccessorNameFail :: Property
299+
testAccessorNameFail = expectFailure $ property $
300+
accessorName s1 == s1FieldName
301+
where
302+
s1FieldName = "Field"
303+
304+
-- | Test the typeName of a struct (should succeed).
305+
testTypeNameOk :: Property
306+
testTypeNameOk = property $
307+
typeName sampleS == s1TypeName
308+
309+
where
310+
311+
sampleS :: S
312+
sampleS = S (Field 0)
313+
314+
s1TypeName :: String
315+
s1TypeName = "S"
316+
317+
-- | Test the typeName of a struct (should fail).
318+
testTypeNameFail :: Property
319+
testTypeNameFail = expectFailure $ property $
320+
typeName sampleS == s1TypeName
321+
322+
where
323+
324+
sampleS :: S
325+
sampleS = S (Field 0)
326+
327+
s1TypeName :: String
328+
s1TypeName = "s"
329+
330+
-- | Auxiliary struct defined for testing purposes.
331+
data S = S { s1 :: Field "field" Int8 }
332+
333+
instance Struct S where
334+
typeName _ = "S"
335+
336+
toValues s = [ Value Int8 (s1 s) ]
337+
338+
instance Typed S where
339+
typeOf = Struct (S (Field 0))

copilot-core/tests/Test/Copilot/Core/Type/Array.hs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import Data.Proxy (Proxy (..))
99
import GHC.TypeNats (KnownNat, natVal)
1010
import Test.Framework (Test, testGroup)
1111
import Test.Framework.Providers.QuickCheck2 (testProperty)
12-
import Test.QuickCheck (Gen, Property, arbitrary, forAll,
13-
vectorOf)
12+
import Test.QuickCheck (Gen, Property, arbitrary,
13+
expectFailure, forAll, property,
14+
vector, vectorOf)
1415

1516
-- Internal imports: library modules being tested
1617
import Copilot.Core.Type.Array (Array, array, arrayElems)
@@ -25,6 +26,10 @@ tests =
2526
(testArrayElemsLeft (Proxy :: Proxy 5))
2627
, testProperty "arrayElems . array (identity; 200)"
2728
(testArrayElemsLeft (Proxy :: Proxy 200))
29+
, testProperty "array of incorrect length"
30+
testArrayElemsFail
31+
, testProperty "Show for arrays"
32+
testShowArray
2833
]
2934

3035
-- * Individual tests
@@ -43,3 +48,15 @@ testArrayElemsLeft len =
4348
-- Generator for lists of Int64 of known length.
4449
xsInt64 :: Gen [Int64]
4550
xsInt64 = vectorOf (fromIntegral (natVal len)) arbitrary
51+
52+
-- | Test that arrays cannot be properly evaluated if their length does not
53+
-- match their type.
54+
testArrayElemsFail :: Property
55+
testArrayElemsFail = expectFailure $ forAll (vector 3) $ \l ->
56+
let v = array l :: Array 4 Int64
57+
in arrayElems v == l
58+
59+
-- | Test show for arrays.
60+
testShowArray :: Property
61+
testShowArray = forAll (vector 3) $ \l -> property $
62+
show (array l :: Array 3 Int64) == show (l :: [Int64])

0 commit comments

Comments
 (0)