LDAPv3-0.1.2.0: test/Arbitrary.hs
-- Copyright (c) 2019 Herbert Valerio Riedel <hvr@gnu.org>
--
-- This file is free software: you may copy, redistribute and/or modify it
-- under the terms of the GNU General Public License as published by the
-- Free Software Foundation, either version 2 of the License, or (at your
-- option) any later version.
--
-- This file is distributed in the hope that it will be useful, but
-- WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program (see `LICENSE`). If not, see
-- <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Arbitrary () where
import LDAPv3.ASN1String
import LDAPv3.Message
import LDAPv3.OID
import LDAPv3.StringRepr (DistinguishedName (..))
import qualified Data.ByteString as BS
import qualified Data.Char as C
import Data.Coerce (coerce)
import Data.Int
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe (fromJust)
import Data.Proxy (Proxy (..))
import Data.String (fromString)
import qualified Data.Text.Short as TS
import Data.Word
import Test.QuickCheck.Instances ()
import Test.Tasty.QuickCheck
instance Arbitrary TS.ShortText where
arbitrary = TS.fromText <$> arbitrary
shrink t = map TS.fromText (shrink (TS.toText t))
-- instance Arbitrary x => Arbitrary (IMPLICIT tag x) where
-- arbitrary = IMPLICIT <$> arbitrary
-- shrink (IMPLICIT x) = coerce (shrink x)
-- instance Arbitrary x => Arbitrary (EXPLICIT tag x) where
-- arbitrary = EXPLICIT <$> arbitrary
-- shrink (EXPLICIT x) = coerce (shrink x)
instance Arbitrary x => Arbitrary (SET x) where
arbitrary = SET <$> arbitrary
shrink (SET x) = coerce (shrink x)
instance Arbitrary x => Arbitrary (SET1 x) where
arbitrary = SET1 <$> arbitrary
shrink (SET1 x) = coerce (shrink x)
instance Arbitrary MessageID where
arbitrary = MessageID <$> arbitrary
shrink (MessageID i) = coerce (shrink i)
instance Arbitrary (UInt 1 127 Int8) where
arbitrary = either (\_ -> 1) id . toUInt <$> choose (1,127)
instance Arbitrary (UInt 0 MaxInt Int32) where
arbitrary = int2msgid <$> arbitrary
shrink = map int2msgid . shrink . fromUInt
int2msgid :: Int32 -> UInt 0 MaxInt Int32
int2msgid = either (\_ -> 0) id . toUInt . abs
instance Arbitrary ResultCode where
arbitrary = arbitraryBoundedEnum
shrink ResultCode'success = []
shrink _ = [ResultCode'success]
instance Arbitrary LDAPMessage where
arbitrary = LDAPMessage <$> arbitrary <*> arbitrary <*> arbitrary
shrink = genericShrink
-- instance Arbitrary (BOOLEAN_DEFAULT b) where
-- arbitrary = BOOLEAN <$> arbitrary
instance Arbitrary Control where
arbitrary = Control <$> arbitrary <*> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary LDAPResult where
arbitrary = LDAPResult <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary ProtocolOp where
arbitrary = frequency
[ (2, ProtocolOp'bindRequest <$> arbitrary)
, (2, ProtocolOp'bindResponse <$> arbitrary)
, (1, ProtocolOp'unbindRequest <$> arbitrary)
, (5, ProtocolOp'searchRequest <$> arbitrary)
, (1, ProtocolOp'searchResDone <$> arbitrary)
, (5, ProtocolOp'searchResEntry <$> arbitrary)
, (2, ProtocolOp'searchResRef <$> arbitrary)
, (2, ProtocolOp'modifyRequest <$> arbitrary)
, (1, ProtocolOp'modifyResponse <$> arbitrary)
, (2, ProtocolOp'addRequest <$> arbitrary)
, (1, ProtocolOp'addResponse <$> arbitrary)
, (1, ProtocolOp'delRequest <$> arbitrary)
, (1, ProtocolOp'delResponse <$> arbitrary)
, (2, ProtocolOp'modDNRequest <$> arbitrary)
, (1, ProtocolOp'modDNResponse <$> arbitrary)
, (2, ProtocolOp'compareRequest <$> arbitrary)
, (1, ProtocolOp'compareResponse <$> arbitrary)
, (1, ProtocolOp'abandonRequest <$> arbitrary)
, (2, ProtocolOp'extendedReq <$> arbitrary)
, (2, ProtocolOp'extendedResp <$> arbitrary)
, (2, ProtocolOp'intermediateResponse <$> arbitrary)
]
shrink = genericShrink
instance Arbitrary BindRequest where
arbitrary = BindRequest <$> arbitrary <*> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary AuthenticationChoice where
arbitrary = oneof [ AuthenticationChoice'simple <$> arbitrary
, AuthenticationChoice'sasl <$> arbitrary
]
shrink = genericShrink
instance Arbitrary SaslCredentials where
arbitrary = SaslCredentials <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary BindResponse where
arbitrary = BindResponse <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary SearchRequest where
arbitrary = SearchRequest <$> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
shrink = genericShrink
instance Arbitrary Scope where
arbitrary = arbitraryBoundedEnum
shrink = genericShrink
instance Arbitrary DerefAliases where
arbitrary = arbitraryBoundedEnum
shrink = genericShrink
instance Arbitrary Filter where
arbitrary = frequency
[( 1, Filter'and <$> arbitrary)
,( 1, Filter'or <$> arbitrary)
,( 1, Filter'not <$> arbitrary)
,(100, Filter'equalityMatch <$> arbitrary)
,(100, Filter'substrings <$> arbitrary)
,(100, Filter'greaterOrEqual <$> arbitrary)
,(100, Filter'lessOrEqual <$> arbitrary)
,(100, Filter'present <$> arbitrary)
,(100, Filter'approxMatch <$> arbitrary)
,(100, Filter'extensibleMatch <$> arbitrary)
]
shrink = genericShrink
instance Arbitrary SubstringFilter where
arbitrary = SubstringFilter <$> arbitrary <*> sub'arbitrary
where
sub'arbitrary = do
initial <- oneof [ ((:[]) . Substring'initial) <$> nonEmptyBS, pure [] ]
final <- oneof [ ((:[]) . Substring'final) <$> nonEmptyBS, pure [] ]
anys <- case (initial,final) of
([],[]) -> listOf nonEmptyBS `suchThat` (not . null)
_ -> listOf nonEmptyBS
case (initial ++ map Substring'any anys ++ final) of
[] -> error "the impossible just happened"
(x:xs) -> pure (x:|xs)
nonEmptyBS = arbitrary `suchThat` (not . BS.null)
shrink = genericShrink
instance Arbitrary MatchingRuleAssertion where
arbitrary = do
_MatchingRuleAssertion'matchingRule <- arbitrary
-- /If the @matchingRule@ field is absent, the @type@ field MUST be present/
_MatchingRuleAssertion'type <- case _MatchingRuleAssertion'matchingRule of
Just _ -> arbitrary
Nothing -> Just <$> arbitrary
_MatchingRuleAssertion'matchValue <- arbitrary
_MatchingRuleAssertion'dnAttributes <- arbitrary
pure MatchingRuleAssertion {..}
shrink = genericShrink
instance Arbitrary Substring where
arbitrary = oneof [ Substring'initial <$> nonEmptyBS
, Substring'any <$> nonEmptyBS
, Substring'final <$> nonEmptyBS
]
where
nonEmptyBS = arbitrary `suchThat` (not . BS.null)
-- shrink = genericShrink
instance Arbitrary SearchResultEntry where
arbitrary = SearchResultEntry <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary PartialAttribute where
arbitrary = PartialAttribute <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary Attribute where
arbitrary = Attribute <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary SearchResultReference where
arbitrary = SearchResultReference <$> arbitrary
shrink = genericShrink
instance Arbitrary ModifyRequest where
arbitrary = ModifyRequest <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary Change where
arbitrary = Change <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary Operation where
arbitrary = arbitraryBoundedEnum
shrink = genericShrink
instance Arbitrary AddRequest where
arbitrary = AddRequest <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary CompareRequest where
arbitrary = CompareRequest <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary AttributeValueAssertion where
arbitrary = AttributeValueAssertion <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary ModifyDNRequest where
arbitrary = ModifyDNRequest <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary ExtendedRequest where
arbitrary = ExtendedRequest <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary ExtendedResponse where
arbitrary = ExtendedResponse <$> arbitrary <*> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary IntermediateResponse where
arbitrary = IntermediateResponse <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary AttributeDescription where
arbitrary = AttributeDescription <$> arbitrary <*> arbitrary
shrink = genericShrink
instance Arbitrary MatchingRuleId where
arbitrary = MatchingRuleId <$> (arbitrary `suchThat` (/= Left "dn")) -- avoid grammar ambiguity
shrink = genericShrink
instance Arbitrary KeyString where
arbitrary = fromString <$> ((:) <$> a'leadkeychar <*> listOf a'keychar)
instance Arbitrary Option where
arbitrary = fromString <$> (listOf a'keychar `suchThat` (not . null))
a'keychar :: Gen Char
a'keychar = choose ('-', 'z') `suchThat` (\c -> C.isAsciiUpper c || C.isAsciiLower c || C.isDigit c || c == '-')
a'leadkeychar :: Gen Char
a'leadkeychar = choose ('A', 'z') `suchThat` C.isLetter
-- generates only well-formed subset
instance Arbitrary OID where
arbitrary = (OID . fmap fromIntegral) <$> oneof
[ (0 :|) <$> ((:) <$> ch40 <*> arbitrary)
, (1 :|) <$> ((:) <$> ch40 <*> arbitrary)
, (2 :|) <$> ((:) <$> arbitrary <*> arbitrary)
]
where
ch40 = choose (0 :: Word64, 39)
instance Arbitrary OBJECT_IDENTIFIER where
arbitrary = fromJust . object_identifier'fromOID <$> arbitrary
instance Arbitrary DistinguishedName where
shrink (DistinguishedName rdns) = map DistinguishedName $ genericShrink rdns
arbitrary = (DistinguishedName <$> arbitrary) `suchThat` nonEmptyOctets
where
nonEmptyOctets (DistinguishedName rdns) = all (all ok) rdns
where
ok = either (not . BS.null) (const True) . snd
instance Arbitrary ASN1StringChoice where
arbitrary = oneof
[ ASN1String'OCTET_STRING <$> arbitrary
, ASN1String'UniversalString <$> arbitrary
, ASN1String'UTF8String <$> arbitrary
, ASN1String'BMPString <$> arbitrary
, ASN1String'IA5String <$> arbitrary
, ASN1String'VisibleString <$> arbitrary
, ASN1String'PrintableString <$> arbitrary
, ASN1String'NumericString <$> arbitrary
]
instance Arbitrary UniversalString where
arbitrary = (asn1string'fromCodePoints <$> arbitrary) `suchThatMap` id
instance Arbitrary BMPString where
arbitrary = bmpString'fromUcs2CodePoints <$> arbitrary
instance Arbitrary IA5String where
arbitrary = a'ia5subtype
instance Arbitrary VisibleString where
arbitrary = a'ia5subtype
instance Arbitrary LDAPv3.ASN1String.PrintableString where
arbitrary = a'ia5subtype
instance Arbitrary NumericString where
arbitrary = a'ia5subtype
a'ia5subtype :: forall s . ASN1String s => Gen s
a'ia5subtype = do
s <- asn1string'fromCodePoints <$> listOf a'char
case s of
Nothing -> error "internal error in ia5subtype generator"
Just x -> pure x
where
a'hasChar = asn1string'supportsCodePoint (Proxy :: Proxy s)
a'char = choose (min'char, max'char) `suchThat` a'hasChar
max'char = last $ filter a'hasChar ['\0'..'\x7f']
min'char = head $ filter a'hasChar ['\0'..'\x7f']