packages feed

duckling-0.1.0.0: tests/Duckling/Api/Tests.hs

-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.


{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}

module Duckling.Api.Tests (tests) where

import qualified Data.HashMap.Strict as HashMap
import qualified Data.HashSet as HashSet
import Data.List (sortOn)
import Data.Text (Text)
import Prelude
import Test.Tasty
import Test.Tasty.HUnit

import Duckling.Api
import Duckling.Dimensions.Types
import Duckling.Lang
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Testing.Asserts
import Duckling.Testing.Types
import Duckling.Types

tests :: TestTree
tests = testGroup "API Tests"
  [ parseTest
  , rankTest
  , rangeTest
  , supportedDimensionsTest
  ]

parseTest :: TestTree
parseTest = testCase "Parse Test" $
  case parse sentence testContext [This Numeral] of
    [] -> assertFailure "empty result"
    (Entity dim body value start end:_) -> do
      assertEqual "dim" "number" dim
      assertEqual "body" "42" body
      assertEqual "value" val value
      assertEqual "start" 4 start
      assertEqual "end" 6 end
  where
    sentence = "hey 42 there"
    val = toJText TNumeral.NumeralValue {TNumeral.vValue = 42.0}

rankTest :: TestTree
rankTest = testGroup "Rank Tests"
  [ rankFilterTest
  , rankOrderTest
  ]

rankFilterTest :: TestTree
rankFilterTest = testCase "Rank Filter Tests" $ do
  mapM_ check
    [ ( "in 2 minutes"
      , [This Numeral, This Duration, This Time]
      , [This Time]
      )
    , ( "in 2 minutes, about 42 degrees"
      , [This Numeral, This Temperature, This Time]
      , [This Time, This Temperature]
      )
    , ( "today works... and tomorrow at 9pm too"
      , [This Numeral, This Time]
      , [This Time, This Time]
      )
    , ( "between 9:30 and 11:00 on thursday or Saturday and Thanksgiving Day"
      , [This Numeral, This Time]
      , [This Time, This Time, This Time]
      )
    , ("the day after tomorrow 5pm", [This Time], [This Time])
    , ("the day after tomorrow 5pm", [This Time, This Numeral], [This Time])
    , ("the day after tomorrow 5pm", [], [This Time])
    ]
  where
    check :: (Text, [Some Dimension], [Some Dimension]) -> IO ()
    check (sentence, targets, expected) =
      let go = analyze sentence testContext $ HashSet.fromList targets
          actual = flip map go $
                     \(Resolved{node=Node{token=Token d _}}) -> This d
      in assertEqual ("wrong winners for " ++ show sentence) expected actual

rankOrderTest :: TestTree
rankOrderTest = testCase "Rank Order Tests" $ do
  mapM_ check
    [ ("tomorrow at 5PM or 8PM", [This Time])
    , ("321 12 3456 ... 7", [This Numeral])
    , ("42 today 23 tomorrow", [This Numeral, This Time])
    ]
  where
    check (s, targets) =
      let tokens = analyze s testContext $ HashSet.fromList targets
        in assertEqual "wrong ordering" (sortOn range tokens) tokens

rangeTest :: TestTree
rangeTest = testCase "Range Tests" $ do
  mapM_ (analyzedFirstTest testContext) xs
  where
    xs = map (\(input, targets, range) -> (input, targets, f range))
             [ ( "order status 3233763377", [This PhoneNumber], Range 13 23 )
             , ( "  3233763377  "         , [This PhoneNumber], Range  2 12 )
             , ( " -3233763377"           , [This PhoneNumber], Range  2 12 )
             , ( "  now"                  , [This Time]       , Range  2  5 )
             , ( "   Monday  "            , [This Time]       , Range  3  9 )
             , ( "  next   week "         , [This Time]       , Range  2 13 )
             , ( "   42\n\n"              , [This Numeral]    , Range  3  5 )
             ]
    f :: Range -> TestPredicate
    f expected _ (Resolved {range = actual}) = expected == actual

supportedDimensionsTest :: TestTree
supportedDimensionsTest = testCase "Supported Dimensions Test" $ do
  mapM_ check
    [ ( AR
      , [ This Email, This AmountOfMoney, This PhoneNumber, This Url
        , This Numeral, This Ordinal
        ]
      )
    , ( PL
      , [ This Email, This AmountOfMoney, This PhoneNumber, This Url
        , This Duration, This Numeral, This Ordinal, This Time
        ]
      )
    ]
  where
    check :: (Lang, [Some Dimension]) -> IO ()
    check (l, expected) = case HashMap.lookup l supportedDimensions of
      Nothing -> assertFailure $ "no dimensions for " ++ show l
      Just actual ->
        assertEqual ("wrong dimensions for " ++ show l) expected actual