Revision 3ce788db htest/Test/Ganeti/Query/Language.hs

b/htest/Test/Ganeti/Query/Language.hs
34 34
import Test.QuickCheck
35 35

  
36 36
import Control.Applicative
37
import Control.Arrow (second)
38
import Text.JSON
37 39

  
38 40
import Test.Ganeti.TestHelper
39 41
import Test.Ganeti.TestCommon
40 42

  
41
import qualified Ganeti.Query.Language as Qlang
43
import Ganeti.Query.Language
42 44

  
43
-- | Custom 'Qlang.Filter' generator (top-level), which enforces a
45
-- | Custom 'Filter' generator (top-level), which enforces a
44 46
-- (sane) limit on the depth of the generated filters.
45
genFilter :: Gen (Qlang.Filter Qlang.FilterField)
47
genFilter :: Gen (Filter FilterField)
46 48
genFilter = choose (0, 10) >>= genFilter'
47 49

  
48 50
-- | Custom generator for filters that correctly halves the state of
49 51
-- the generators at each recursive step, per the QuickCheck
50 52
-- documentation, in order not to run out of memory.
51
genFilter' :: Int -> Gen (Qlang.Filter Qlang.FilterField)
53
genFilter' :: Int -> Gen (Filter FilterField)
52 54
genFilter' 0 =
53
  oneof [ return Qlang.EmptyFilter
54
        , Qlang.TrueFilter     <$> getName
55
        , Qlang.EQFilter       <$> getName <*> value
56
        , Qlang.LTFilter       <$> getName <*> value
57
        , Qlang.GTFilter       <$> getName <*> value
58
        , Qlang.LEFilter       <$> getName <*> value
59
        , Qlang.GEFilter       <$> getName <*> value
60
        , Qlang.RegexpFilter   <$> getName <*> arbitrary
61
        , Qlang.ContainsFilter <$> getName <*> value
55
  oneof [ pure EmptyFilter
56
        , TrueFilter     <$> getName
57
        , EQFilter       <$> getName <*> value
58
        , LTFilter       <$> getName <*> value
59
        , GTFilter       <$> getName <*> value
60
        , LEFilter       <$> getName <*> value
61
        , GEFilter       <$> getName <*> value
62
        , RegexpFilter   <$> getName <*> arbitrary
63
        , ContainsFilter <$> getName <*> value
62 64
        ]
63
    where value = oneof [ Qlang.QuotedString <$> getName
64
                        , Qlang.NumericValue <$> arbitrary
65
    where value = oneof [ QuotedString <$> getName
66
                        , NumericValue <$> arbitrary
65 67
                        ]
66 68
genFilter' n = do
67
  oneof [ Qlang.AndFilter  <$> vectorOf n'' (genFilter' n')
68
        , Qlang.OrFilter   <$> vectorOf n'' (genFilter' n')
69
        , Qlang.NotFilter  <$> genFilter' n'
69
  oneof [ AndFilter  <$> vectorOf n'' (genFilter' n')
70
        , OrFilter   <$> vectorOf n'' (genFilter' n')
71
        , NotFilter  <$> genFilter' n'
70 72
        ]
71 73
  where n' = n `div` 2 -- sub-filter generator size
72 74
        n'' = max n' 2 -- but we don't want empty or 1-element lists,
73 75
                       -- so use this for and/or filter list length
74 76

  
75
$(genArbitrary ''Qlang.ItemType)
77
$(genArbitrary ''ItemType)
76 78

  
77
instance Arbitrary Qlang.FilterRegex where
78
  arbitrary = getName >>= Qlang.mkRegex -- a name should be a good regex
79
instance Arbitrary FilterRegex where
80
  arbitrary = getName >>= mkRegex -- a name should be a good regex
81

  
82
$(genArbitrary ''ResultStatus)
83

  
84
$(genArbitrary ''FieldType)
85

  
86
$(genArbitrary ''FieldDefinition)
87

  
88
-- | Generates an arbitrary JSValue. We do this via a function a not
89
-- via arbitrary instance since that would require us to define an
90
-- arbitrary for JSValue, which can be recursive, entering the usual
91
-- problems with that; so we only generate the base types, not the
92
-- recursive ones, and not 'JSNull', which we can't use in a
93
-- 'RSNormal' 'ResultEntry'.
94
genJSValue :: Gen JSValue
95
genJSValue = do
96
  oneof [ JSBool <$> arbitrary
97
        , JSRational <$> pure False <*> arbitrary
98
        , JSString <$> (toJSString <$> arbitrary)
99
        , (JSArray . map showJSON) <$> (arbitrary::Gen [Int])
100
        , JSObject . toJSObject . map (second showJSON) <$>
101
          (arbitrary::Gen [(String, Int)])
102
        ]
103

  
104
-- | Generates a 'ResultEntry' value.
105
genResultEntry :: Gen ResultEntry
106
genResultEntry = do
107
  rs <- arbitrary
108
  rv <- case rs of
109
          RSNormal -> Just <$> genJSValue
110
          _ -> pure Nothing
111
  return $ ResultEntry rs rv
112

  
113
$(genArbitrary ''QueryFieldsResult)
79 114

  
80 115
-- | Tests that serialisation/deserialisation of filters is
81 116
-- idempotent.
82
prop_Serialisation :: Property
83
prop_Serialisation = forAll genFilter testSerialisation
117
prop_filter_serialisation :: Property
118
prop_filter_serialisation = forAll genFilter testSerialisation
84 119

  
85
prop_FilterRegex_instances :: Qlang.FilterRegex -> Property
86
prop_FilterRegex_instances rex =
120
-- | Tests that filter regexes are serialised correctly.
121
prop_filterregex_instances :: FilterRegex -> Property
122
prop_filterregex_instances rex =
87 123
  printTestCase "failed JSON encoding" (testSerialisation rex) .&&.
88 124
  printTestCase "failed read/show instances" (read (show rex) ==? rex)
89 125

  
126
-- | Tests 'ResultStatus' serialisation.
127
prop_resultstatus_serialisation :: ResultStatus -> Property
128
prop_resultstatus_serialisation = testSerialisation
129

  
130
-- | Tests 'FieldType' serialisation.
131
prop_fieldtype_serialisation :: FieldType -> Property
132
prop_fieldtype_serialisation = testSerialisation
133

  
134
-- | Tests 'FieldDef' serialisation.
135
prop_fielddef_serialisation :: FieldDefinition -> Property
136
prop_fielddef_serialisation = testSerialisation
137

  
138
-- | Tests 'ResultEntry' serialisation. Needed especially as this is
139
-- done manually, and not via buildObject (different serialisation
140
-- format).
141
prop_resultentry_serialisation :: Property
142
prop_resultentry_serialisation = forAll genResultEntry testSerialisation
143

  
144
-- | Tests 'FieldDef' serialisation. We use a made-up maximum limit of
145
-- 20 for the generator, since otherwise the lists become too long and
146
-- we don't care so much about list length but rather structure.
147
prop_fieldsresult_serialisation :: Property
148
prop_fieldsresult_serialisation =
149
  forAll (resize 20 arbitrary::Gen QueryFieldsResult) testSerialisation
150

  
90 151
testSuite "Query/Language"
91
  [ 'prop_Serialisation
92
  , 'prop_FilterRegex_instances
152
  [ 'prop_filter_serialisation
153
  , 'prop_filterregex_instances
154
  , 'prop_resultstatus_serialisation
155
  , 'prop_fieldtype_serialisation
156
  , 'prop_fielddef_serialisation
157
  , 'prop_resultentry_serialisation
158
  , 'prop_fieldsresult_serialisation
93 159
  ]

Also available in: Unified diff