packages feed

fuzzystrmatch-pg-0.1.0.0: test/spec/LevenshteinSpec.hs

module LevenshteinSpec where

import qualified Data.Text as T

import Data.Text (Text)
import Data.FuzzyStrMatch.Levenshtein

import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck.Instances () -- imports instances only
import Prelude

empty :: Text
empty = ""

kitten :: Text
kitten = "kitten"

sitting :: Text
sitting = "sitting"

-- TODO: Test very large strings
spec :: Spec
spec = do
  describe "Test Levenshtein Distance" $ do
    context "should be 0 when inputs are equal" $ do
      prop "when both are non-empty strings" $ do
        \txt -> levenshtein txt txt `shouldBe` 0

      it "when both are empty strings" $
        levenshtein empty empty `shouldBe` 0

    context "distance should be equal to expectation" $ do
      it "should be 3 between kitten and sitting" $
        levenshtein kitten sitting `shouldBe` 3

      prop "when one argument is empty, distance should be length of other arg" $ do
        \source -> levenshtein source empty == (T.length source)

    context "order of arguments should not matter" $ do
      prop "application is commutative" $
        \source target -> levenshtein source target == (levenshtein target source)

    context "levenshtein with different costs" $ do
      it "insertion cost is doubled" $
        levenshteinWithCosts empty kitten 2 1 1 `shouldBe` 12 -- 6 insertions, so we get 12

      it "deletion cost is doubled" $
        levenshteinWithCosts kitten empty 1 2 1 `shouldBe` 12 -- 6 deletions, so we get 12

      it "substitution cost is doubled" $
        levenshteinWithCosts kitten sitting 1 1 2 `shouldBe` 5 -- 2 substitutions + 1 deletion, so we get (2 * 2) + 1 = 5

      prop "if arguments order is switched, the result is same when switching insert and delete cost" $
        \source target cost -> if cost > 0 then levenshteinWithCosts source target cost 1 1 == levenshteinWithCosts target source 1 cost 1 else True


    -- ================================
    -- Tests for "levenshteinLessEqual"
    -- ================================
    context "levenshtein less equal" $ do
      prop "for all successful result, it should be less than maxD" $
        \source target maxD ->
          case levenshteinLessEqual source target maxD of
            Just d -> d <= maxD
            Nothing -> True

      prop "for all successful result, it should be equal to their levenstehin" $
        \source target maxD ->
          case levenshteinLessEqual source target maxD of
            Just d -> d == levenshtein source target
            Nothing -> True

      it "test on kitten and sitting example" $ do
        levenshteinLessEqual kitten sitting 3 `shouldBe` (Just 3)
        levenshteinLessEqual kitten sitting 2 `shouldBe` Nothing