packages feed

bcp47-0.3.0.0: tests/Data/BCP47Spec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Data.BCP47Spec
  ( spec
  ) where

import TestImport

import Country.Identifier (china)
import Data.BCP47
import Data.BCP47.Internal.Extension
import Data.BCP47.Internal.LanguageExtension
import Data.BCP47.Internal.PrivateUse
import Data.BCP47.Internal.Script
import Data.BCP47.Internal.Variant
import Data.Either (isRight)
import Data.LanguageCodes (ISO639_1 (ZH))
import qualified Data.Set as Set

spec :: Spec
spec = do
  describe "fromText" $ do
    it "parses all components" $ do
      lng <-
        fromTextThrows
          "zh-abc-def-zxy-Hant-CN-1967-y-extensi-x-private1-private2"
      language lng `shouldBe` ZH
      extendedLanguageSubtags lng
        `shouldBe` Set.singleton (LanguageExtension "abc-def-zxy")
      script lng `shouldBe` Just (Script "Hant")
      region lng `shouldBe` Just china
      variants lng `shouldBe` Set.singleton (Variant "1967")
      extensions lng `shouldBe` Set.singleton (Extension "y-extensi")
      privateUse lng
        `shouldBe` Set.fromList [PrivateUse "private1", PrivateUse "private2"]

    it "only parses complete subtags" $ do
      -- Specifically, the region CN should not be parsed out of the variant CNUVWXYX
      lng <- fromTextThrows "zh-CNUVWXYX"
      language lng `shouldBe` ZH
      extendedLanguageSubtags lng `shouldBe` Set.empty
      script lng `shouldBe` Nothing
      region lng `shouldBe` Nothing
      variants lng `shouldBe` Set.singleton (Variant "CNUVWXYX")
      privateUse lng `shouldBe` Set.empty

  describe "Arbitrary"
    . it "can parse arbitrary generated tags"
    . property
    $ \tag -> fromText (toText tag) `shouldBe` Right tag

  describe "Read/Show" . it "can roundtrip" . property $ \tag ->
    readMaybe (show @BCP47 tag) `shouldBe` Just tag

  describe "Eq" $ do
    it "compares equal with different casing" $ do
      lower <-
        fromTextThrows
          "zh-abc-def-zxy-hant-cn-1967-y-extensi-x-private1-private2"
      upper <-
        fromTextThrows
          "ZH-ABC-DEF-ZXY-HANT-CN-1967-Y-EXTENSI-X-PRIVATE1-PRIVATE2"
      upper `shouldBe` lower

  describe "Regression tests" $ do
    -- We used to require Region to be uppercase (incorrectly)
    it "accepts en-gb" $ fromText "en-gb" `shouldBe` Right enGB

  describe "Known bugs" $ do
    -- Per https://datatracker.ietf.org/doc/html/rfc5646#section-2.2.4
    -- we should accept e.g. 419 (Latin America and the Caribbean), but
    -- not 3-digit M49 codes for regions that already have a 2-letter
    -- code, e.g. 012 (Algeria, DZ)
    --
    -- The country library we use only recognizes 3-digit M49 codes for
    -- countries, so it rejects other kinds of regions.
    it "accepts es-419" $ do
      pendingWith "country library does not recognize non-country M49 codes"
      let result = fromText "es-419"
      result `shouldSatisfy` isRight

    -- Per https://www.iso.org/obp/ui/#iso:code:3166:TP
    -- East Timor changed to Timor-Leste, but TP is still reserved
    -- until 2052-05.
    --
    -- The country library we use only recognizes Timor-Leste (TL),
    -- so it rejects TP.
    it "accepts en-TP" $ do
      pendingWith "country library does not recognize TP anymore"
      let result = fromText "es-TP"
      result `shouldSatisfy` isRight

    -- Per https://datatracker.ietf.org/doc/html/rfc5646#section-2.2.2
    -- Languages encompassed by certain macrolanguages can be
    -- registered as extended language subtags. These may be represented
    -- either with the lone subtag for the encompassed language, e.g.
    -- cmn for Mandarin, or with a language-extlang combination, e.g.
    -- zh-cmn. The first option is preferred.
    --
    -- We currently don't support this because we always expect a
    -- leading ISO-639-1 code.
    it "accepts cnm" $ do
      pendingWith "Parser incorrectly requires a leading ISO-639-1 code"
      let result = fromText "cnm"
      result `shouldSatisfy` isRight