Write Grammar tests

parser
Bram 2024-04-10 18:14:32 +02:00
parent 203205f53c
commit b3e103a5d9
4 changed files with 204 additions and 9 deletions

View File

@ -1,6 +1,6 @@
module Internal.Grammar.ServerName exposing module Internal.Grammar.ServerName exposing
( ServerName, toString, fromString ( ServerName, toString, fromString
, servernameParser , serverNameParser
) )
{-| {-|
@ -83,9 +83,11 @@ dnsNameParser =
|> P.getChompedString |> P.getChompedString
{-| Convert a string to a server name.
-}
fromString : String -> Maybe ServerName fromString : String -> Maybe ServerName
fromString s = fromString s =
P.run (servernameParser |. P.end) s P.run (serverNameParser |. P.end) s
|> (\out -> |> (\out ->
case out of case out of
Ok _ -> Ok _ ->
@ -212,8 +214,11 @@ portParser =
) )
servernameParser : Parser ServerName {-| Parse a server name. Generally used by other identifiers that have a server
servernameParser = name as one of its parts.
-}
serverNameParser : Parser ServerName
serverNameParser =
P.succeed ServerName P.succeed ServerName
|= hostnameParser |= hostnameParser
|= P.oneOf |= P.oneOf
@ -224,6 +229,8 @@ servernameParser =
] ]
{-| Convert a parsed server name back to a string.
-}
toString : ServerName -> String toString : ServerName -> String
toString { host, port_ } = toString { host, port_ } =
let let

View File

@ -1,4 +1,7 @@
module Internal.Grammar.UserId exposing (..) module Internal.Grammar.UserId exposing
( UserID, toString, fromString
, userIdParser, isHistorical
)
{-| {-|
@ -36,6 +39,16 @@ localparts from the expanded character set:
extended_user_id_char = %x21-39 / %x3B-7E ; all ASCII printing chars except : extended_user_id_char = %x21-39 / %x3B-7E ; all ASCII printing chars except :
## User ID
@docs UserID, toString, fromString
## Extra
@docs userIdParser, isHistorical
-} -}
import Internal.Grammar.ServerName as ServerName exposing (ServerName) import Internal.Grammar.ServerName as ServerName exposing (ServerName)
@ -43,13 +56,39 @@ import Internal.Tools.ParserExtra as PE
import Parser as P exposing ((|.), (|=), Parser) import Parser as P exposing ((|.), (|=), Parser)
{-| The User ID type defining a user.
-}
type alias UserID = type alias UserID =
{ localpart : String, domain : ServerName } { localpart : String, domain : ServerName }
{-| Convert a Matrix User ID back into its uniquely identifying string.
-}
fromString : String -> Maybe UserID fromString : String -> Maybe UserID
fromString = fromString =
P.run userIdParser >> Result.toMaybe P.run (userIdParser |. P.end) >> Result.toMaybe
{-| Return a boolean on whether a Matrix user has a historical user ID.
Since this user ID is not SUPPOSED to be legal but clients are nevertheless
forced to support them due to backwards compatibility, clients may occasionally
attempt to break the rules in an attempt to find undefined behaviour.
As a result, an explicit method to spot historical users is added to the SDK.
-}
isHistorical : UserID -> Bool
isHistorical { localpart } =
String.any
(\c ->
let
i : Int
i =
Char.toCode c
in
not ((0x61 <= i && i <= 0x7A) || Char.isAlpha c)
)
localpart
localpartParser : Parser String localpartParser : Parser String
@ -60,18 +99,23 @@ localpartParser =
|> P.map String.concat |> P.map String.concat
{-| Convert a parsed User ID to a string.
-}
toString : UserID -> String toString : UserID -> String
toString { localpart, domain } = toString { localpart, domain } =
String.concat [ "@", localpart, ":", ServerName.toString domain ] String.concat [ "@", localpart, ":", ServerName.toString domain ]
{-| Parse a UserID from a string.
-}
userIdParser : Parser UserID userIdParser : Parser UserID
userIdParser = userIdParser =
P.succeed UserID P.succeed UserID
|. P.symbol "@" |. P.symbol "@"
|= localpartParser |= localpartParser
|. P.symbol ":" |. P.symbol ":"
|= ServerName.servernameParser |= ServerName.serverNameParser
|> PE.maxLength 255
validHistoricalUsernameChar : Char -> Bool validHistoricalUsernameChar : Char -> Bool

View File

@ -101,8 +101,7 @@ suite =
(\server -> (\server ->
SN.fromString server SN.fromString server
|> Maybe.map SN.toString |> Maybe.map SN.toString
|> Maybe.map (String.replace "::" ":") |> Expect.equal (Just server)
|> Expect.equal (Just <| String.replace "::" ":" server)
) )
, test "Checking spec examples" , test "Checking spec examples"
(\() -> (\() ->

View File

@ -0,0 +1,145 @@
module Test.Grammar.UserId exposing (..)
import Expect
import Fuzz exposing (Fuzzer)
import Internal.Grammar.UserId as U
import Test exposing (..)
import Test.Grammar.ServerName as ServerName
modernUserCharFuzzer : Fuzzer Char
modernUserCharFuzzer =
Fuzz.oneOf
[ Fuzz.intRange 0x61 0x7A
|> Fuzz.map Char.fromCode
, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|> String.toList
|> Fuzz.oneOfValues
]
historicalUserCharFuzzer : Fuzzer Char
historicalUserCharFuzzer =
[ ( 0x21, 0x39 ), ( 0x3B, 0x7E ) ]
|> List.map (\( low, high ) -> Fuzz.intRange low high)
|> Fuzz.oneOf
|> Fuzz.map Char.fromCode
modernUserFuzzer : Fuzzer String
modernUserFuzzer =
Fuzz.map2
(\localpart domain ->
let
maxLocalSize : Int
maxLocalSize =
255 - String.length domain - 2
in
localpart
|> List.take maxLocalSize
|> String.fromList
|> (\l -> "@" ++ l ++ ":" ++ domain)
)
(Fuzz.listOfLengthBetween 1 255 modernUserCharFuzzer)
(ServerName.serverNameFuzzer
|> Fuzz.filter
(\name ->
String.length name < 255 - 2
)
)
historicalUserFuzzer : Fuzzer String
historicalUserFuzzer =
Fuzz.map2
(\localpart domain ->
let
maxLocalSize : Int
maxLocalSize =
255 - String.length domain - 2
in
localpart
|> List.take maxLocalSize
|> String.fromList
|> (\l -> "@" ++ l ++ ":" ++ domain)
)
(Fuzz.listOfLengthBetween 1 255 historicalUserCharFuzzer)
(ServerName.serverNameFuzzer
|> Fuzz.filter
(\name ->
String.length name < 255 - 2
)
)
userFuzzer : Fuzzer String
userFuzzer =
Fuzz.oneOf [ modernUserFuzzer, historicalUserFuzzer ]
suite : Test
suite =
describe "UserId"
[ describe "Size"
[ fuzz ServerName.serverNameFuzzer
"Username cannot be length 0"
(\domain ->
"@"
++ ":"
++ domain
|> U.fromString
|> Expect.equal Nothing
)
, fuzz2 (Fuzz.listOfLengthBetween 1 255 historicalUserCharFuzzer)
ServerName.serverNameFuzzer
"Username length cannot exceed 255"
(\localpart domain ->
let
username : String
username =
"@"
++ String.fromList localpart
++ ":"
++ domain
in
Expect.equal
(U.fromString username == Nothing)
(String.length username > 255)
)
, fuzz modernUserFuzzer
"Modern fuzzer has appropriate size"
(String.length >> Expect.lessThan 256)
, fuzz historicalUserFuzzer
"Historical fuzzer has appropriate size"
(String.length >> Expect.lessThan 256)
]
, describe "From string evaluation"
[ fuzz userFuzzer
"fromString always returns a value on fuzzer"
(U.fromString >> Expect.notEqual Nothing)
, fuzz userFuzzer
"fromString -> toString returns the same value"
(\username ->
username
|> U.fromString
|> Maybe.map U.toString
|> Expect.equal (Just username)
)
, fuzz historicalUserFuzzer
"Historical users are historical"
(\username ->
username
|> U.fromString
|> Maybe.map U.isHistorical
|> Expect.equal (Just True)
)
, fuzz modernUserFuzzer
"Modern users are not historical"
(\username ->
username
|> U.fromString
|> Maybe.map U.isHistorical
|> Expect.equal (Just False)
)
]
]