packages feed

glean-0.1.0.0: glean/test/tests/Angle/DerivedTest.hs

{-
  Copyright (c) Meta Platforms, Inc. and affiliates.
  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.
-}

{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeApplications #-}
module Angle.DerivedTest (main) where

import Data.List
import Data.Text (Text)
import Test.HUnit

import TestRunner
import Util.String.Quasi

import Glean.Init
import Glean.Query.Thrift as Thrift
import qualified Glean.Schema.GleanTest.Types as Glean.Test
import Glean.Typed hiding (end)

import TestDB

main :: IO ()
main = withUnitTest $ testRunner $ TestList
  [ TestLabel "derived" $ derivedTest id
  , TestLabel "derived/page" $ derivedTest (limit 1)
  ]

-- | Tests for derived predicates using Angle syntax. See
-- JSONQueryTest.jsonDerivedTest for the Thrift-query-type versions of
-- these tests.
derivedTest :: (forall a . Query a -> Query a) -> Test
derivedTest modify = dbTestCase $ \env repo -> do
  -- Several tests with the trivial derived predicate: IsThree ::= 3
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.IsThree
    [s|
      glean.test.IsThree _
    |]
  print results
  assertEqual "derived - IsThree 1" 1 (length results)

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.IsThree
    [s|
      Y where Y = glean.test.IsThree X; X = 4
    |]
  print results
  assertEqual "derived - IsThree 2" 0 (length results)

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.IsThree
    [s|
      glean.test.IsThree 3
    |]
  print results
  assertEqual "derived - IsThree 3" 1 (length results)

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.IsThree
    [s|
      glean.test.IsThree 4
    |]
  print results
  assertEqual "derived - IsThree 4" 0 (length results)

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.IsThree
    [s|
      X = 3; glean.test.IsThree X
    |]
  print results
  assertEqual "derived - IsThree 5" 1 (length results)

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.IsThree
    [s|
      X = 4; glean.test.IsThree X
    |]
  print results
  assertEqual "derived - IsThree 6" 0 (length results)

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.IsGlean
    [s|
      glean.test.IsGlean "gl"..
    |]
  print results
  assertEqual "derived - IsGlean 1" 1 (length results)

  -- Tests with RevStringPair
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevStringPair
    [s|
      glean.test.RevStringPair _
    |]
  print results
  assertBool "derived 1" $ length results == 6

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevStringPair
    [s|
      glean.test.RevStringPair {"a",_}
    |]
  print results
  assertBool "derived 2" $ length results == 1

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevStringPair
    [s|
      glean.test.RevStringPair {_,"a"}
    |]
  print results
  assertBool "derived 3" $ case results of
    [one] | Just key <- getFactKey one ->
      "a" == Glean.Test.revStringPair_key_snd key
    _otherwise -> False

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevStringPair
    [s|
      glean.test.RevStringPair {X,X}
    |]
  print results
  assertBool "derived 4" $ length results == 2

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevRevStringPair
    [s|
      glean.test.RevRevStringPair _
    |]
  print results
  assertBool "derived 5" $ length results == 6

  -- The Ids of derived fact better be distinct, even when we page the results
  assertBool "derived 5: distinct Ids" $ length (nub (map getId results)) == 6

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevRevStringPair
    [s|
      glean.test.RevRevStringPair {"a",_}
    |]
  print results
  assertBool "derived 6" $ case results of
    [one] | Just key <- getFactKey one ->
      "a" == Glean.Test.revRevStringPair_key_fst key
    _otherwise -> False

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.ReflStringPair
    [s|
      glean.test.ReflStringPair "x"..
    |]
  print results
  assertBool "derived 7" $ length results == 1

  -- check that we can recursively expand the results of a derived query
  results <- runQuery_ env repo $ modify $ recursive $
    angle @Glean.Test.DualStringPair
    [s|
      glean.test.DualStringPair _
    |]
  print results
  assertBool "derived 8" $ (==4) $ length
    [ fst
    | Just key <- map getFactKey results
    , Just fst <- [getFactKey $ Glean.Test.dualStringPair_key_fst key] ]

  -- a pattern on the LHS of a statement that arises due to
  -- substitution of a derived predicate
  results <- runQuery_ env repo $ modify $ recursive $
    angle @Glean.Test.DualStringPair
    [s|
      P = glean.test.StringPair _; glean.test.DualStringPair {P,_}
    |]
  print results
  assertBool "derived 10" $ (==4) $ length results

  -- More complicated matching
  results <- runQuery_ env repo $ modify $ recursive $
    angle @Glean.Test.RevStringPair
    [s|
      glean.test.StringPair X;
      glean.test.RevStringPair X
      # generates all RevStringPairs and matches each one against X
    |]
  print results
  assertEqual "derived match filter" 4 (length results)

  -- deriving a key-only predicate from a key-value predicate
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.DerivedKeyValue
    [s|
      glean.test.DerivedKeyValue _
    |]
  print results
  assertEqual "angle - derived key/value 1" 3 (length results)

  -- match on the key
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.DerivedKeyValue
    [s|
      glean.test.DerivedKeyValue { kstring = "foo" }
    |]
  print results
  assertEqual "angle - derived key/value 2" 2 (length results)

  -- match on the value
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.DerivedKeyValue
    [s|
      glean.test.DerivedKeyValue { vnat = 5 }
    |]
  print results
  assertEqual "angle - derived key/value 3" 1 (length results)

  -- deriving a key-value predicate from a key-only predicate which is
  -- derived from a key-value predicate
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.DerivedKeyValue2
    [s|
      glean.test.DerivedKeyValue2 _
    |]
  print results
  assertEqual "angle - derived key/value 4" 3 (length results)

  -- match on the key
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.DerivedKeyValue2
    [s|
      glean.test.DerivedKeyValue2 { kstring = "foo" } -> _
    |]
  print results
  assertEqual "angle - derived key/value 5" 2 (length results)

  -- match on the value
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.DerivedKeyValue2
    [s|
      glean.test.DerivedKeyValue2 _ -> { vnat = 5 }
    |]
  print results
  assertEqual "angle - derived key/value 6" 1 (length results)

  -- a derived predicate defined as a record, not a tuple
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevStringPairRec
    [s|
      glean.test.RevStringPairRec {_, "a"}
    |]
  print results
  assertBool "angle - derived with record" $ length results == 1

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevStringPairRec
    [s|
      glean.test.RevStringPairRec { snd = "a" }
    |]
  print results
  assertBool "angle - derived with record 2" $ length results == 1

  -- tests that require inserting derived facts into the temporary environment
  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevStringPair
    [s|
      X where X = glean.test.RevStringPair _
    |]
  print results
  assertEqual "angle - tmp environment" 6 (length results)

  results <- runQuery_ env repo $ modify $ angle @Glean.Test.RevStringPair
    [s|
       P where
       P = glean.test.RevStringPair {A,"a"};
       glean.test.RevStringPair {_,A}
    |]
  print results
  assertEqual "angle - tmp environment 2" 1 (length results)

  results <- runQuery_ env repo $ modify $ recursive $
    angle @Glean.Test.RevStringPairs
    [s|
      glean.test.RevStringPairs { x = "a" }
    |]
  print results
  assertEqual "angle - tmp environment 3" 1 (length results)

  -- non-bindable or-pattern in the key of a derived predicate. This
  -- is a tricky case to handle: the query engine will have to expand
  -- and substitute the derived predicate twice.
  results <- runQuery_ env repo $ modify $ recursive $
    angle @Glean.Test.RevStringPairs
    [s|
      glean.test.RevStringPairs { x = "a".. | "b".. }
    |]
  print results
  assertEqual "angle - derived + or pattern" 2 (length results)

  results <- runQuery_ env repo $ modify $ recursive $
    angleData @[Text]
    [s|
      [X1, X2] where glean.test.RevStringPair{X1, X2};
    |]
  print results
  assertEqual "angle - derived returning array" 6 (length results)

  -- sum type in the derived predicate head
  results <- runQuery_ env repo $ modify $ recursive $
    angle @Glean.Test.MatchOneAlt
    [s|
      glean.test.MatchOneAlt { { tue = 3 }, _ }
    |]
  print results
  assertEqual "angle - MatchOneAlt 1" 1 (length results)

  results <- runQuery_ env repo $ modify $ recursive $
    angle @Glean.Test.MatchOneAlt
    [s|
      glean.test.MatchOneAlt { { mon = 3 }, _ }
    |]
  print results
  assertEqual "angle - MatchOneAlt 2" 0 (length results)

  -- Test for a bug
  r <- runQuery_ env repo $ modify $ angle @Glean.Test.LeftOr $
    [s|
      glean.test.LeftOr { x = "c".. }
    |]
  print r
  assertEqual "angle - LeftOr" 1 (length r)

  r <- runQuery_ env repo $ modify $ angle @Glean.Test.LeftOr2 $
    [s|
      glean.test.LeftOr2 { x = "c".. }
    |]
  print r
  assertEqual "angle - LeftOr2" 1 (length r)

  -- Test for another bug
  results <- runQuery_ env repo $ modify $ recursive $
    angle @Glean.Test.RevStringPairs
    [s|
      glean.test.RevStringPairs { X, {_, X} }
    |]
  print results
  assertEqual "angle - RevStringPairs " 6 (length results)

  -- flipping statements
  r <- runQuery_ env repo $ modify $ angle @Glean.Test.Unbound2
    [s|
      glean.test.Unbound2 { _, "e" }
    |]
  print r
  assertEqual "angle - flip statement 1" 1 (length r)

  r <- runQuery_ env repo $ modify $ angle @Glean.Test.Unbound2
    [s|
      glean.test.Unbound2 { "e", _ }
    |]
  print r
  assertEqual "angle - flip statement 2" 1 (length r)

  -- test for repeated variables in the derived predicate
  r <- runQuery_ env repo $ modify $ angle @Glean.Test.SameString
    [s|
      glean.test.SameString { "a", "b" }
    |]
  print r
  assertEqual "angle - repeated vars 1" 0 (length r)

  r <- runQuery_ env repo $ modify $ angle @Glean.Test.SameString
    [s|
      glean.test.SameString { "a", "a" }
    |]
  print r
  assertEqual "angle - repeated vars 2" 1 (length r)

  -- unification
  r <- runQuery_ env repo $ modify $ angle @Glean.Test.Unbound2
    [s|
      glean.test.Unbound2 _
    |]
  print r
  assertEqual "angle - unification" 2 (length r)

  -- or-statements and derived predicates
  r <- runQuery_ env repo $ modify $ angleData @Text
    [s|
      N where
        glean.test.RevStringPair { N, _ } |
        glean.test.RevRevStringPair { N, _ }
    |]
  assertEqual "angle - or-statements derived" 8 (length (nub r))