Add subsetOf Filter function

pull/17/head
Bram 2024-01-04 03:13:17 +01:00
parent 3739043f87
commit e8ee125def
4 changed files with 266 additions and 84 deletions

View File

@ -3,6 +3,7 @@ module Internal.Filter.Timeline exposing
, pass, onlySenders, allSendersExcept, onlyTypes, allTypesExcept, fail , pass, onlySenders, allSendersExcept, onlyTypes, allTypesExcept, fail
, match, run , match, run
, and , and
, subsetOf
) )
{-| {-|
@ -33,6 +34,11 @@ for interacting with the Matrix API.
@docs and @docs and
## Compare
@docs subsetOf
-} -}
import Set exposing (Set) import Set exposing (Set)
@ -132,15 +138,15 @@ and (Filter f1) (Filter f2) =
( False, False ) -> ( False, False ) ->
Set.intersect f1.types f2.types Set.intersect f1.types f2.types
, typesAllowOthers = f2.typesAllowOthers && f2.typesAllowOthers , typesAllowOthers = f1.typesAllowOthers && f2.typesAllowOthers
} }
in in
case stdAnd of case stdAnd of
Filter f -> Filter f ->
if Set.isEmpty f.senders && (not f.sendersAllowOthers) then if Set.isEmpty f.senders && not f.sendersAllowOthers then
fail fail
else if Set.isEmpty f.types && (not f.typesAllowOthers) then else if Set.isEmpty f.types && not f.typesAllowOthers then
fail fail
else else
@ -235,3 +241,36 @@ pass =
run : Filter -> List (Event a) -> List (Event a) run : Filter -> List (Event a) -> List (Event a)
run f events = run f events =
List.filter (match f) events List.filter (match f) events
{-| Determine whether the second argument is a subset filter of the first
argument.
-}
subsetOf : Filter -> Filter -> Bool
subsetOf (Filter big) (Filter small) =
let
isSSof : Set String -> Set String -> Bool
isSSof b s =
Set.intersect b s == s
isSubsetFor : ( Bool, Set String ) -> ( Bool, Set String ) -> Bool
isSubsetFor ( bb, sb ) ( bs, ss ) =
case ( bb, bs ) of
( True, True ) ->
isSSof ss sb
( True, False ) ->
Set.isEmpty (Set.intersect sb ss)
( False, True ) ->
False
( False, False ) ->
isSSof sb ss
in
isSubsetFor
( big.sendersAllowOthers, big.senders )
( small.sendersAllowOthers, small.senders )
&& isSubsetFor
( big.typesAllowOthers, big.types )
( small.typesAllowOthers, small.types )

View File

@ -1,7 +1,8 @@
module Internal.Values.Event exposing module Internal.Values.Event exposing
( Event ( Event
, UnsignedData(..), age, prevContent, redactedBecause, transactionId , UnsignedData(..), age, prevContent, redactedBecause, transactionId
, encode, decoder, isEqual , encode, decoder
, isEqual
) )
{-| {-|
@ -24,6 +25,7 @@ of a room.
@docs encode, decoder @docs encode, decoder
## Test functions ## Test functions
@docs isEqual @docs isEqual
@ -133,16 +135,22 @@ isEqual : Event -> Event -> Bool
isEqual e1 e2 = isEqual e1 e2 =
if e1.eventId /= e2.eventId then if e1.eventId /= e2.eventId then
False False
else if e1.originServerTs /= e2.originServerTs then else if e1.originServerTs /= e2.originServerTs then
False False
else if e1.roomId /= e2.roomId then else if e1.roomId /= e2.roomId then
False False
else if e1.sender /= e2.sender then else if e1.sender /= e2.sender then
False False
else if e1.stateKey /= e2.stateKey then else if e1.stateKey /= e2.stateKey then
False False
else if e1.eventType /= e2.eventType then else if e1.eventType /= e2.eventType then
False False
else else
case ( e1.unsigned, e2.unsigned ) of case ( e1.unsigned, e2.unsigned ) of
( Nothing, Nothing ) -> ( Nothing, Nothing ) ->
@ -157,10 +165,13 @@ isEqual e1 e2 =
( Just (UnsignedData d1), Just (UnsignedData d2) ) -> ( Just (UnsignedData d1), Just (UnsignedData d2) ) ->
if d1.age /= d2.age then if d1.age /= d2.age then
False False
else if d1.transactionId /= d2.transactionId then else if d1.transactionId /= d2.transactionId then
False False
else if Maybe.map (E.encode 0) d1.prevContent /= Maybe.map (E.encode 0) d2.prevContent then else if Maybe.map (E.encode 0) d1.prevContent /= Maybe.map (E.encode 0) d2.prevContent then
False False
else else
case ( d1.redactedBecause, d2.redactedBecause ) of case ( d1.redactedBecause, d2.redactedBecause ) of
( Nothing, Nothing ) -> ( Nothing, Nothing ) ->
@ -175,6 +186,7 @@ isEqual e1 e2 =
( Just se1, Just se2 ) -> ( Just se1, Just se2 ) ->
isEqual se1 se2 isEqual se1 se2
{-| Determine the previous `content` value for this event. This field is only a {-| Determine the previous `content` value for this event. This field is only a
`Just value` if the event is a state event, and the Matrix Vault has permission `Just value` if the event is a state event, and the Matrix Vault has permission
to see the previous content. to see the previous content.

View File

@ -4,6 +4,7 @@ import Expect
import Fuzz exposing (Fuzzer) import Fuzz exposing (Fuzzer)
import Internal.Filter.Timeline as Filter exposing (Filter) import Internal.Filter.Timeline as Filter exposing (Filter)
import Internal.Values.Event as Event import Internal.Values.Event as Event
import Set
import Test exposing (..) import Test exposing (..)
import Test.Values.Event as TestEvent import Test.Values.Event as TestEvent
@ -138,6 +139,138 @@ suite =
|> Filter.and (Filter.allTypesExcept types) |> Filter.and (Filter.allTypesExcept types)
|> Expect.equal Filter.fail |> Expect.equal Filter.fail
) )
, fuzz2 (Fuzz.list Fuzz.string)
(Fuzz.list Fuzz.string)
"Only list + all except list = common types"
(\t1 t2 ->
Expect.equal
(Filter.and
(Filter.onlyTypes t1)
(Filter.allTypesExcept t2)
)
(Set.diff (Set.fromList t1) (Set.fromList t2)
|> Set.toList
|> Filter.onlyTypes
)
)
, fuzz2 (Fuzz.list Fuzz.string)
(Fuzz.list Fuzz.string)
"Only list + all except list = common senders"
(\t1 t2 ->
Expect.equal
(Filter.and
(Filter.onlySenders t1)
(Filter.allSendersExcept t2)
)
(Set.diff (Set.fromList t1) (Set.fromList t2)
|> Set.toList
|> Filter.onlySenders
)
)
]
, describe "Subset testing"
[ fuzz2 fuzzer
fuzzer
"Combining two filters is always a subset"
(\filter1 filter2 ->
filter1
|> Filter.and filter2
|> Expect.all
[ Filter.subsetOf filter1 >> Expect.equal True
, Filter.subsetOf filter2 >> Expect.equal True
]
)
, fuzz
(Fuzz.bool
|> Fuzz.andThen
(\same ->
if same then
Fuzz.map (\a -> ( a, a )) fuzzer
else
Fuzz.map2 Tuple.pair fuzzer fuzzer
)
)
"subset goes both way iff equal"
(\( filter1, filter2 ) ->
Expect.equal
(filter1 == filter2)
(Filter.subsetOf filter1 filter2
&& Filter.subsetOf filter2 filter1
)
)
, fuzz2 Fuzz.string
(Fuzz.list Fuzz.string)
"One more excluded sender is a subset"
(\head tail ->
Filter.allSendersExcept (head :: tail)
|> Filter.subsetOf (Filter.allSendersExcept tail)
|> Expect.equal True
)
, fuzz2 Fuzz.string
(Fuzz.list Fuzz.string)
"One more excluded type is a subset"
(\head tail ->
Filter.allTypesExcept (head :: tail)
|> Filter.subsetOf (Filter.allTypesExcept tail)
|> Expect.equal True
)
, fuzz2 Fuzz.string
(Fuzz.list Fuzz.string)
"One less included sender is a subset"
(\head tail ->
Filter.onlySenders tail
|> Filter.subsetOf (Filter.onlySenders (head :: tail))
|> Expect.equal True
)
, fuzz2 Fuzz.string
(Fuzz.list Fuzz.string)
"One less included type is a subset"
(\head tail ->
Filter.onlyTypes tail
|> Filter.subsetOf (Filter.onlyTypes (head :: tail))
|> Expect.equal True
)
, fuzz3 Fuzz.string
(Fuzz.list Fuzz.string)
fuzzer
"One more excluded sender is a subset - even when combined with another fuzzer"
(\head tail filter ->
Filter.allSendersExcept (head :: tail)
|> Filter.and filter
|> Filter.subsetOf (Filter.and filter <| Filter.allSendersExcept tail)
|> Expect.equal True
)
, fuzz3 Fuzz.string
(Fuzz.list Fuzz.string)
fuzzer
"One more excluded type is a subset - even when combined with another fuzzer"
(\head tail filter ->
Filter.allTypesExcept (head :: tail)
|> Filter.and filter
|> Filter.subsetOf (Filter.and filter <| Filter.allTypesExcept tail)
|> Expect.equal True
)
, fuzz3 Fuzz.string
(Fuzz.list Fuzz.string)
fuzzer
"One less included sender is a subset - even when combined with another fuzzer"
(\head tail filter ->
Filter.onlySenders tail
|> Filter.and filter
|> Filter.subsetOf (Filter.and filter <| Filter.onlySenders (head :: tail))
|> Expect.equal True
)
, fuzz3 Fuzz.string
(Fuzz.list Fuzz.string)
fuzzer
"One less included type is a subset - even when combined with another fuzzer"
(\head tail filter ->
Filter.onlyTypes tail
|> Filter.and filter
|> Filter.subsetOf (Filter.and filter <| Filter.onlyTypes (head :: tail))
|> Expect.equal True
)
] ]
, describe "Use case testing" , describe "Use case testing"
[ fuzz3 (Fuzz.list TestEvent.fuzzer) [ fuzz3 (Fuzz.list TestEvent.fuzzer)
@ -157,13 +290,12 @@ suite =
l2 : List Event.Event l2 : List Event.Event
l2 = l2 =
( List.filter List.filter
(\e -> (\e ->
(List.member e.sender senders) && List.member e.sender senders
(List.member e.eventType types) && List.member e.eventType types
) )
events events
)
in in
Expect.all Expect.all
[ Expect.equal (List.length l1) (List.length l2) [ Expect.equal (List.length l1) (List.length l2)
@ -192,13 +324,12 @@ suite =
l2 : List Event.Event l2 : List Event.Event
l2 = l2 =
( List.filter List.filter
(\e -> (\e ->
(List.member e.sender senders) && List.member e.sender senders
(not <| List.member e.eventType types) && (not <| List.member e.eventType types)
) )
events events
)
in in
Expect.all Expect.all
[ Expect.equal (List.length l1) (List.length l2) [ Expect.equal (List.length l1) (List.length l2)
@ -227,13 +358,12 @@ suite =
l2 : List Event.Event l2 : List Event.Event
l2 = l2 =
( List.filter List.filter
(\e -> (\e ->
(not <| List.member e.sender senders) && (not <| List.member e.sender senders)
(List.member e.eventType types) && List.member e.eventType types
) )
events events
)
in in
Expect.all Expect.all
[ Expect.equal (List.length l1) (List.length l2) [ Expect.equal (List.length l1) (List.length l2)
@ -262,13 +392,12 @@ suite =
l2 : List Event.Event l2 : List Event.Event
l2 = l2 =
( List.filter List.filter
(\e -> (\e ->
(not <| List.member e.sender senders) && (not <| List.member e.sender senders)
(not <| List.member e.eventType types) && (not <| List.member e.eventType types)
) )
events events
)
in in
Expect.all Expect.all
[ Expect.equal (List.length l1) (List.length l2) [ Expect.equal (List.length l1) (List.length l2)

View File

@ -1,11 +1,11 @@
module Test.Values.Event exposing (..) module Test.Values.Event exposing (..)
import Expect
import Fuzz exposing (Fuzzer) import Fuzz exposing (Fuzzer)
import Internal.Values.Event as Event exposing (Event) import Internal.Values.Event as Event exposing (Event)
import Json.Encode as E import Json.Encode as E
import Test exposing (..) import Test exposing (..)
import Test.Tools.Timestamp as TestTimestamp import Test.Tools.Timestamp as TestTimestamp
import Expect
fuzzer : Fuzzer Event fuzzer : Fuzzer Event
@ -67,10 +67,12 @@ valueFuzzer =
, Fuzz.map Event.encode (Fuzz.lazy (\_ -> fuzzer)) , Fuzz.map Event.encode (Fuzz.lazy (\_ -> fuzzer))
] ]
suite : Test suite : Test
suite = suite =
describe "Sanity check" describe "Sanity check"
[ fuzz fuzzer "event = event" [ fuzz fuzzer
"event = event"
(\event -> (\event ->
Event.isEqual event event Event.isEqual event event
|> Expect.equal True |> Expect.equal True