packages feed

bishbosh-0.1.1.0: src-test/BishBosh/Test/QuickCheck/ContextualNotation/PGN.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE CPP #-}
{-
	Copyright (C) 2018 Dr. Alistair Ward

	This file is part of BishBosh.

	BishBosh 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.

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

 [@DESCRIPTION@]	Implements 'Test.QuickCheck.Arbitrary' & defines /QuickCheck/-properties.
-}

module BishBosh.Test.QuickCheck.ContextualNotation.PGN(
-- * Types
-- ** Type-synonyms
	PGN,
-- * Constants
--	ficsGamesDBGameNoTag,
--	ecoCodeTag,
	results
-- * Functions
--	arbitraryECOCode
) where

import			BishBosh.Test.QuickCheck.ContextualNotation.PGNComment()
import qualified	BishBosh.ContextualNotation.PGN			as ContextualNotation.PGN
import qualified	BishBosh.ContextualNotation.PGNComment		as ContextualNotation.PGNComment
import qualified	BishBosh.ContextualNotation.StandardAlgebraic	as ContextualNotation.StandardAlgebraic
import qualified	BishBosh.Model.Game				as Model.Game
import qualified	BishBosh.Test.QuickCheck.Model.Game		as Test.QuickCheck.Model.Game
import qualified	BishBosh.Type.Length				as Type.Length
import qualified	Data.Word
import qualified	Test.QuickCheck

#ifndef USE_POLYPARSE
import			Control.Arrow((|||))
import qualified	Text.ParserCombinators.Parsec
#elif USE_POLYPARSE == 1
import qualified	Text.ParserCombinators.Poly.Lazy	as Poly
#else /* Plain */
import			Control.Arrow((|||))
import qualified	Text.ParserCombinators.Poly.Plain	as Poly
#endif

-- | Qualifies a value.
ficsGamesDBGameNoTag :: ContextualNotation.PGN.Tag
ficsGamesDBGameNoTag	= "FICSGamesDBGameNo"

-- | Qualifies a value.
ecoCodeTag :: ContextualNotation.PGN.Tag
ecoCodeTag		= "ECO"

-- | Returns an arbitrary ECO-code.
arbitraryECOCode :: Test.QuickCheck.Gen String
arbitraryECOCode	= do
	c	<- Test.QuickCheck.elements ['A' .. 'E']
	i	<- Test.QuickCheck.elements ['0' .. '9']
	j	<- Test.QuickCheck.elements ['0' .. '9']

	return {-to Gen-monad-} [c, i, j]

-- | Defines a concrete type for testing.
type PGN	= ContextualNotation.PGN.PGN Type.Length.X Type.Length.Y

instance (
	Enum	x,
	Enum	y,
	Ord	x,
	Ord	y,
	Show	x,
	Show	y
 ) => Test.QuickCheck.Arbitrary (ContextualNotation.PGN.PGN x y) where
	{-# SPECIALISE instance Test.QuickCheck.Arbitrary PGN #-}
	arbitrary	= let
		arbitraryString :: Test.QuickCheck.Gen String
		arbitraryString	= do
			s	<- filter (
				`notElem` ['\\', '\n', '\r', ContextualNotation.PGN.quoteDelimiter]
			 ) <$> Test.QuickCheck.arbitrary

			return {-to Gen-monad-} $ if s == [ContextualNotation.PGN.unknownTagValue]
				then '\\' : s
				else s

		arbitraryWord :: Test.QuickCheck.Gen String
		arbitraryWord	= (show :: Data.Word.Word -> String) <$> Test.QuickCheck.arbitrary

		arbitraryMaybeWord :: Test.QuickCheck.Gen (Maybe String)
		arbitraryMaybeWord	= Test.QuickCheck.oneof [
			return {-to Gen-monad-} Nothing,
			fmap Just arbitraryWord
		 ]
	 in ContextualNotation.PGN.mkPGN <$> fmap Just arbitraryString {-Event-} <*> fmap Just arbitraryString {-Site-} <*> fmap (
		toEnum . abs
	 ) Test.QuickCheck.arbitrary {-Day-} <*> arbitraryMaybeWord {-Round-} <*> fmap Just arbitraryString {-White-} <*> fmap Just arbitraryString {-Black-} <*> (
		do
			ficsGamesDBGameNo	<- arbitraryWord
			ecoCode			<- arbitraryECOCode

			return {-to Gen-monad-} [
				(
					ficsGamesDBGameNoTag,	ficsGamesDBGameNo
				), (
					ecoCodeTag,		ecoCode
				)
			 ] -- Optional tags.
	 ) <*> Test.QuickCheck.arbitrary {-Game-}

-- | The constant test-results for this data-type.
results :: IO [Test.QuickCheck.Result]
results	= sequence [
	let
		f :: Bool -> ContextualNotation.StandardAlgebraic.ValidateMoves -> Test.QuickCheck.Model.Game.Game -> Test.QuickCheck.Property
		f isStrictlySequential validateMoves game	= Test.QuickCheck.label "PGN.prop_moveTextParser" .
#ifdef USE_POLYPARSE
#	if USE_POLYPARSE == 1
			(Model.Game.listTurns game ==) . Model.Game.listTurns
#	else /* Plain */
			(const False ||| (Model.Game.listTurns game ==) . Model.Game.listTurns)
#	endif
			. fst {-discard unparsed text-} . Poly.runParser moveTextParser
#else /* Parsec */
			(const False ||| (Model.Game.listTurns game ==) . Model.Game.listTurns) . Text.ParserCombinators.Parsec.parse moveTextParser "Move-text parser"
#endif
			$ ContextualNotation.PGN.showsMoveText game ""
			where
				moveTextParser	= ContextualNotation.PGN.moveTextParser isStrictlySequential validateMoves
	in Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 128 } f,
	let
		f :: Bool -> ContextualNotation.StandardAlgebraic.ValidateMoves -> PGN -> [ContextualNotation.PGNComment.PGNComment] -> Test.QuickCheck.Property
		f isStrictlySequential validateMoves pgn pgnComments	= Test.QuickCheck.label "PGN.prop_pgnParser" .
#ifdef USE_POLYPARSE
#	if USE_POLYPARSE == 1
			(== pgn)
#	else /* Plain */
			(const False ||| (== pgn))
#	endif
			. fst {-discard unparsed text-} . Poly.runParser parser
#else /* Parsec */
			(const False ||| (== pgn)) . Text.ParserCombinators.Parsec.parse parser "PGN parser"
#endif
			. unlines . (
				\l -> zipWith (++) l $ map (showChar '\t' . show) pgnComments ++ repeat "" {-pad comment-list with null lines-}
			) . lines $ show pgn
			where
				parser	= ContextualNotation.PGN.parser isStrictlySequential validateMoves [ficsGamesDBGameNoTag, ecoCodeTag]
	in Test.QuickCheck.quickCheckWithResult Test.QuickCheck.stdArgs { Test.QuickCheck.maxSuccess = 1024 } f
 ]