{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE QuasiQuotes #-} module OsPathSpec where import Data.Maybe import System.OsPath as OSP import System.OsString.Internal.Types import System.OsPath.Posix as Posix import System.OsPath.Windows as Windows import System.OsPath.Encoding import qualified System.OsString.Internal.Types as OS import System.OsPath.Data.ByteString.Short ( toShort ) import System.OsString.Posix as PosixS import System.OsString.Windows as WindowsS import Control.Exception import Data.ByteString ( ByteString ) import qualified Data.ByteString as BS import Test.QuickCheck import Test.QuickCheck.Checkers import qualified Test.QuickCheck.Classes as QC import GHC.IO.Encoding.UTF8 ( mkUTF8 ) import GHC.IO.Encoding.UTF16 ( mkUTF16le ) import GHC.IO.Encoding ( setFileSystemEncoding ) import GHC.IO.Encoding.Failure ( CodingFailureMode(..) ) import Control.DeepSeq import Data.Bifunctor ( first ) import qualified Data.ByteString.Char8 as C import qualified System.OsPath.Data.ByteString.Short.Word16 as BS16 import qualified System.OsPath.Data.ByteString.Short as SBS import Data.Char ( ord ) import Arbitrary fromRight :: b -> Either a b -> b fromRight _ (Right b) = b fromRight b _ = b tests :: [(String, Property)] tests = [ ("OSP.encodeUtf . OSP.decodeUtf == id", property $ \(NonNullString str) -> (OSP.decodeUtf . fromJust . OSP.encodeUtf) str == Just str) , ("decodeUtf . encodeUtf == id (Posix)", property $ \(NonNullString str) -> (Posix.decodeUtf . fromJust . Posix.encodeUtf) str == Just str) , ("decodeUtf . encodeUtf == id (Windows)", property $ \(NonNullString str) -> (Windows.decodeUtf . fromJust . Windows.encodeUtf) str == Just str) , ("encodeWith ucs2le . decodeWith ucs2le == id (Posix)", property $ \(padEven -> bs) -> (Posix.encodeWith ucs2le . (\(Right r) -> r) . Posix.decodeWith ucs2le . OS.PS . toShort) bs === Right (OS.PS . toShort $ bs)) , ("encodeWith ucs2le . decodeWith ucs2le == id (Windows)", property $ \(padEven -> bs) -> (Windows.encodeWith ucs2le . (\(Right r) -> r) . Windows.decodeWith ucs2le . OS.WS . toShort) bs === Right (OS.WS . toShort $ bs)) , ("decodeFS . encodeFS == id (Posix)", property $ \(NonNullString str) -> ioProperty $ do setFileSystemEncoding (mkUTF8 TransliterateCodingFailure) r1 <- Posix.encodeFS str r2 <- try @SomeException $ Posix.decodeFS r1 r3 <- evaluate $ force $ first displayException r2 pure (r3 === Right str) ) , ("decodeFS . encodeFS == id (Windows)", property $ \(NonNullString str) -> ioProperty $ do r1 <- Windows.encodeFS str r2 <- try @SomeException $ Windows.decodeFS r1 r3 <- evaluate $ force $ first displayException r2 pure (r3 === Right str) ) , ("fromPlatformString* functions are equivalent under ASCII (Windows)", property $ \(WindowsString . BS16.pack . map (fromIntegral . ord) . nonNullAsciiString -> str) -> ioProperty $ do r1 <- Windows.decodeFS str r2 <- Windows.decodeUtf str (Right r3) <- pure $ Windows.decodeWith (mkUTF16le TransliterateCodingFailure) str (Right r4) <- pure $ Windows.decodeWith (mkUTF16le RoundtripFailure) str (Right r5) <- pure $ Windows.decodeWith (mkUTF16le ErrorOnCodingFailure) str pure ( r1 === r2 .&&. r1 === r3 .&&. r1 === r4 .&&. r1 === r5 ) ) , ("fromPlatformString* functions are equivalent under ASCII (Posix)", property $ \(PosixString . SBS.toShort . C.pack . nonNullAsciiString -> str) -> ioProperty $ do r1 <- Posix.decodeFS str r2 <- Posix.decodeUtf str (Right r3) <- pure $ Posix.decodeWith (mkUTF8 TransliterateCodingFailure) str (Right r4) <- pure $ Posix.decodeWith (mkUTF8 RoundtripFailure) str (Right r5) <- pure $ Posix.decodeWith (mkUTF8 ErrorOnCodingFailure) str pure ( r1 === r2 .&&. r1 === r3 .&&. r1 === r4 .&&. r1 === r5 ) ) , ("toPlatformString* functions are equivalent under ASCII (Windows)", property $ \(NonNullAsciiString str) -> ioProperty $ do r1 <- Windows.encodeFS str r2 <- Windows.encodeUtf str (Right r3) <- pure $ Windows.encodeWith (mkUTF16le TransliterateCodingFailure) str (Right r4) <- pure $ Windows.encodeWith (mkUTF16le RoundtripFailure) str (Right r5) <- pure $ Windows.encodeWith (mkUTF16le ErrorOnCodingFailure) str pure ( r1 === r2 .&&. r1 === r3 .&&. r1 === r4 .&&. r1 === r5 ) ) , ("toPlatformString* functions are equivalent under ASCII (Posix)", property $ \(NonNullAsciiString str) -> ioProperty $ do r1 <- Posix.encodeFS str r2 <- Posix.encodeUtf str (Right r3) <- pure $ Posix.encodeWith (mkUTF8 TransliterateCodingFailure) str (Right r4) <- pure $ Posix.encodeWith (mkUTF8 RoundtripFailure) str (Right r5) <- pure $ Posix.encodeWith (mkUTF8 ErrorOnCodingFailure) str pure ( r1 === r2 .&&. r1 === r3 .&&. r1 === r4 .&&. r1 === r5 ) ) , ("Unit test toPlatformString* (Posix)", property $ ioProperty $ do let str = "ABcK_(ツ123_&**" let expected = PosixString $ SBS.pack [0x41,0x42,0x63,0x4b,0x5f,0x28,0xe3,0x83,0x84,0x31,0x32,0x33,0x5f,0x26,0x2a,0x2a] r1 <- Posix.encodeFS str r2 <- Posix.encodeUtf str (Right r3) <- pure $ Posix.encodeWith (mkUTF8 TransliterateCodingFailure) str (Right r4) <- pure $ Posix.encodeWith (mkUTF8 RoundtripFailure) str (Right r5) <- pure $ Posix.encodeWith (mkUTF8 ErrorOnCodingFailure) str pure ( r1 === expected .&&. r2 === expected .&&. r3 === expected .&&. r4 === expected .&&. r5 === expected ) ) , ("Unit test toPlatformString* (WindowsString)", property $ ioProperty $ do let str = "ABcK_(ツ123_&**" let expected = WindowsString $ BS16.pack [0x0041,0x0042,0x0063,0x004b,0x005f,0x0028,0x30c4,0x0031,0x0032,0x0033,0x005f,0x0026,0x002a,0x002a] r1 <- Windows.encodeFS str r2 <- Windows.encodeUtf str (Right r3) <- pure $ Windows.encodeWith (mkUTF16le TransliterateCodingFailure) str (Right r4) <- pure $ Windows.encodeWith (mkUTF16le RoundtripFailure) str (Right r5) <- pure $ Windows.encodeWith (mkUTF16le ErrorOnCodingFailure) str pure ( r1 === expected .&&. r2 === expected .&&. r3 === expected .&&. r4 === expected .&&. r5 === expected ) ) , ("Unit test fromPlatformString* (Posix)", property $ ioProperty $ do let bs = PosixString $ SBS.pack [0x41,0x42,0x63,0x4b,0x5f,0x28,0xe3,0x83,0x84,0x31,0x32,0x33,0x5f,0x26,0x2a,0x2a] let expected = "ABcK_(ツ123_&**" r1 <- Posix.decodeFS bs r2 <- Posix.decodeUtf bs (Right r3) <- pure $ Posix.decodeWith (mkUTF8 TransliterateCodingFailure) bs (Right r4) <- pure $ Posix.decodeWith (mkUTF8 RoundtripFailure) bs (Right r5) <- pure $ Posix.decodeWith (mkUTF8 ErrorOnCodingFailure) bs pure ( r1 === expected .&&. r2 === expected .&&. r3 === expected .&&. r4 === expected .&&. r5 === expected ) ) , ("Unit test fromPlatformString* (WindowsString)", property $ ioProperty $ do let bs = WindowsString $ BS16.pack [0x0041,0x0042,0x0063,0x004b,0x005f,0x0028,0x30c4,0x0031,0x0032,0x0033,0x005f,0x0026,0x002a,0x002a] let expected = "ABcK_(ツ123_&**" r1 <- Windows.decodeFS bs r2 <- Windows.decodeUtf bs (Right r3) <- pure $ Windows.decodeWith (mkUTF16le TransliterateCodingFailure) bs (Right r4) <- pure $ Windows.decodeWith (mkUTF16le RoundtripFailure) bs (Right r5) <- pure $ Windows.decodeWith (mkUTF16le ErrorOnCodingFailure) bs pure ( r1 === expected .&&. r2 === expected .&&. r3 === expected .&&. r4 === expected .&&. r5 === expected ) ) , ("QuasiQuoter (WindowsString)", property $ do let bs = WindowsString $ BS16.pack [0x0041,0x0042,0x0063,0x004b,0x005f,0x0028,0x30c4,0x0031,0x0032,0x0033,0x005f,0x0026,0x002a,0x002a] let expected = [WindowsS.pstr|ABcK_(ツ123_&**|] bs === expected ) , ("QuasiQuoter (PosixString)", property $ do let bs = PosixString $ SBS.pack [0x41,0x42,0x63,0x4b,0x5f,0x28,0xe3,0x83,0x84,0x31,0x32,0x33,0x5f,0x26,0x2a,0x2a] let expected = [PosixS.pstr|ABcK_(ツ123_&**|] bs === expected ) , ("QuasiQuoter (WindowsPath)", property $ do let bs = WindowsString $ BS16.pack [0x0041,0x0042,0x0063,0x004b,0x005f] let expected = [Windows.pstr|ABcK_|] bs === expected ) , ("QuasiQuoter (PosixPath)", property $ do let bs = PosixString $ SBS.pack [0x41,0x42,0x63,0x4b,0x5f] let expected = [Posix.pstr|ABcK_|] bs === expected ) , ("pack . unpack == id (Windows)", property $ \ws@(WindowsString _) -> Windows.pack (Windows.unpack ws) === ws ) , ("pack . unpack == id (Posix)", property $ \ws@(PosixString _) -> Posix.pack (Posix.unpack ws) === ws ) , ("pack . unpack == id (OsPath)", property $ \ws@(OsString _) -> OSP.pack (OSP.unpack ws) === ws ) ] ++ testBatch (QC.ord (\(a :: OsPath) -> pure a)) ++ testBatch (QC.monoid (undefined :: OsPath)) ++ testBatch (QC.ord (\(a :: OsString) -> pure a)) ++ testBatch (QC.monoid (undefined :: OsString)) ++ testBatch (QC.ord (\(a :: WindowsString) -> pure a)) ++ testBatch (QC.monoid (undefined :: WindowsString)) ++ testBatch (QC.ord (\(a :: PosixString) -> pure a)) ++ testBatch (QC.monoid (undefined :: PosixString)) ++ testBatch (QC.ord (\(a :: PlatformString) -> pure a)) ++ testBatch (QC.monoid (undefined :: PlatformString)) -- | Allows to insert a 'TestBatch' into a Spec. testBatch :: TestBatch -> [(String, Property)] testBatch (_, tests') = tests' padEven :: ByteString -> ByteString padEven bs | even (BS.length bs) = bs | otherwise = bs `BS.append` BS.pack [70]