packages feed

regexchar-0.9.0.7: src/Grecce/QC/RepeatableMetaChar.hs

{-# LANGUAGE CPP #-}
{-
	Copyright (C) 2010 Dr. Alistair Ward

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program 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.  If not, see <http://www.gnu.org/licenses/>.
-}
{- |
 [@AUTHOR@]	Dr. Alistair Ward

 [@DESCRIPTION@]	Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.
-}

module Grecce.QC.RepeatableMetaChar(
-- * Types
-- ** Data-types
--	RepeatableMetaChar,
-- ** Type-synonyms
--	Testable,
-- * Functions
--	deconstruct,
	quickChecks
) where

import			Control.Applicative((<$>))
import			Grecce.QC.MetaChar()
import qualified	RegExChar.MetaChar		as MetaChar
import qualified	RegExDot.Consumer		as Consumer
import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds
import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile
import qualified	RegExDot.Repeatable		as Repeatable
import qualified	Test.QuickCheck
import qualified	ToolShed.SelfValidate		as SelfValidate

-- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.
newtype RepeatableMetaChar	= MkRepeatableMetaChar (Repeatable.Repeatable MetaChar.MetaChar)	deriving (Eq, Read, Show)

-- | Accessor.
deconstruct :: RepeatableMetaChar -> Repeatable.Repeatable MetaChar.MetaChar
deconstruct (MkRepeatableMetaChar repeatableMetaChar)	= repeatableMetaChar

instance SelfValidate.SelfValidator RepeatableMetaChar	where
	isValid	= SelfValidate.isValid . deconstruct

instance Consumer.Consumer RepeatableMetaChar	where
	consumptionProfile	= Consumer.consumptionProfile . deconstruct
	starHeight		= Consumer.starHeight . deconstruct

instance Test.QuickCheck.Arbitrary RepeatableMetaChar	where
	arbitrary	= do
		metaChar	<- Test.QuickCheck.arbitrary
		fewest		<- Test.QuickCheck.elements [0 .. 9]	--Could be more, but this is an adequate test.
		most		<- Test.QuickCheck.oneof [return Nothing, Just <$> Test.QuickCheck.elements [max fewest 1 .. 9]]
		isGreedy	<- Test.QuickCheck.arbitrary

		let
			repetitionBounds :: Repeatable.RepetitionBounds
			repetitionBounds	= (fewest, most)

		return {-to Gen-monad-} . MkRepeatableMetaChar $ Repeatable.MkRepeatable {
			Repeatable.base			= metaChar,
			Repeatable.repetitionBounds	= repetitionBounds,
			Repeatable.isGreedy		= Repeatable.hasPreciseBounds repetitionBounds || isGreedy	--Only specify 'non-greedy' where space exists.
		}
#if !(MIN_VERSION_QuickCheck(2,1,0))
	coarbitrary	= undefined	--CAVEAT: stops warnings from ghc.
#endif

type Testable	= RepeatableMetaChar -> Test.QuickCheck.Property

-- | Defines invariant properties, which must hold for any 'RepeatableMetaChar'.
quickChecks :: (Testable -> IO ()) -> IO ()
quickChecks checker	= checker `mapM_` [prop_consumptionProfile, prop_io, prop_isValid, prop_starHeight]	where
	prop_consumptionProfile, prop_io, prop_isValid, prop_starHeight :: Testable
	prop_consumptionProfile r	= Test.QuickCheck.label "prop_consumptionProfile" $ (
		case maybeMaxData of
			Nothing	-> True
			Just m	-> m >= minData
	 ) && (
		b == Consumer.getHasSpecificRequirement (Repeatable.base $ deconstruct r)
	 ) where ConsumptionProfile.MkConsumptionProfile {
		ConsumptionProfile.consumptionBounds		= (minData, maybeMaxData),
		ConsumptionProfile.hasSpecificRequirement	= b
	 } = Consumer.consumptionProfile r
	prop_io (MkRepeatableMetaChar r)		= Test.QuickCheck.label "prop_io" $ read (show r) == r
	prop_isValid r					= Test.QuickCheck.label "prop_isValid" $ SelfValidate.isValid r
	prop_starHeight (MkRepeatableMetaChar r)	= Test.QuickCheck.label "prop_starHeight" $ Consumer.starHeight r == if ConsumptionBounds.isPrecise (Consumer.getConsumptionBounds r) then 0 else 1