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
, match, run
, and
, subsetOf
)
{-|
@ -33,6 +34,11 @@ for interacting with the Matrix API.
@docs and
## Compare
@docs subsetOf
-}
import Set exposing (Set)
@ -132,15 +138,15 @@ and (Filter f1) (Filter f2) =
( False, False ) ->
Set.intersect f1.types f2.types
, typesAllowOthers = f2.typesAllowOthers && f2.typesAllowOthers
, typesAllowOthers = f1.typesAllowOthers && f2.typesAllowOthers
}
in
case stdAnd of
Filter f ->
if Set.isEmpty f.senders && (not f.sendersAllowOthers) then
if Set.isEmpty f.senders && not f.sendersAllowOthers then
fail
else if Set.isEmpty f.types && (not f.typesAllowOthers) then
else if Set.isEmpty f.types && not f.typesAllowOthers then
fail
else
@ -235,3 +241,36 @@ pass =
run : Filter -> List (Event a) -> List (Event a)
run 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
( Event
, UnsignedData(..), age, prevContent, redactedBecause, transactionId
, encode, decoder, isEqual
, encode, decoder
, isEqual
)
{-|
@ -24,6 +25,7 @@ of a room.
@docs encode, decoder
## Test functions
@docs isEqual
@ -133,18 +135,24 @@ isEqual : Event -> Event -> Bool
isEqual e1 e2 =
if e1.eventId /= e2.eventId then
False
else if e1.originServerTs /= e2.originServerTs then
False
else if e1.roomId /= e2.roomId then
False
else if e1.sender /= e2.sender then
False
else if e1.stateKey /= e2.stateKey then
False
else if e1.eventType /= e2.eventType then
False
else
case (e1.unsigned, e2.unsigned) of
case ( e1.unsigned, e2.unsigned ) of
( Nothing, Nothing ) ->
True
@ -154,16 +162,19 @@ isEqual e1 e2 =
( Nothing, Just _ ) ->
False
( Just ( UnsignedData d1), Just ( UnsignedData d2 )) ->
( Just (UnsignedData d1), Just (UnsignedData d2) ) ->
if d1.age /= d2.age then
False
else if d1.transactionId /= d2.transactionId then
False
else if Maybe.map (E.encode 0) d1.prevContent /= Maybe.map (E.encode 0) d2.prevContent then
False
else
case (d1.redactedBecause, d2.redactedBecause) of
( Nothing, Nothing) ->
case ( d1.redactedBecause, d2.redactedBecause ) of
( Nothing, Nothing ) ->
True
( Nothing, Just _ ) ->
@ -175,6 +186,7 @@ isEqual e1 e2 =
( Just se1, Just se2 ) ->
isEqual se1 se2
{-| 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
to see the previous content.

View File

@ -4,6 +4,7 @@ import Expect
import Fuzz exposing (Fuzzer)
import Internal.Filter.Timeline as Filter exposing (Filter)
import Internal.Values.Event as Event
import Set
import Test exposing (..)
import Test.Values.Event as TestEvent
@ -138,6 +139,138 @@ suite =
|> Filter.and (Filter.allTypesExcept types)
|> 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"
[ fuzz3 (Fuzz.list TestEvent.fuzzer)
@ -150,20 +283,19 @@ suite =
l1 =
events
|> Filter.run
( Filter.and
( Filter.onlySenders senders )
( Filter.onlyTypes types )
(Filter.and
(Filter.onlySenders senders)
(Filter.onlyTypes types)
)
l2 : List Event.Event
l2 =
( List.filter
List.filter
(\e ->
(List.member e.sender senders) &&
(List.member e.eventType types)
List.member e.sender senders
&& List.member e.eventType types
)
events
)
in
Expect.all
[ Expect.equal (List.length l1) (List.length l2)
@ -185,20 +317,19 @@ suite =
l1 =
events
|> Filter.run
( Filter.and
( Filter.onlySenders senders )
( Filter.allTypesExcept types )
(Filter.and
(Filter.onlySenders senders)
(Filter.allTypesExcept types)
)
l2 : List Event.Event
l2 =
( List.filter
List.filter
(\e ->
(List.member e.sender senders) &&
(not <| List.member e.eventType types)
List.member e.sender senders
&& (not <| List.member e.eventType types)
)
events
)
in
Expect.all
[ Expect.equal (List.length l1) (List.length l2)
@ -220,20 +351,19 @@ suite =
l1 =
events
|> Filter.run
( Filter.and
( Filter.allSendersExcept senders )
( Filter.onlyTypes types )
(Filter.and
(Filter.allSendersExcept senders)
(Filter.onlyTypes types)
)
l2 : List Event.Event
l2 =
( List.filter
List.filter
(\e ->
(not <| List.member e.sender senders) &&
(List.member e.eventType types)
(not <| List.member e.sender senders)
&& List.member e.eventType types
)
events
)
in
Expect.all
[ Expect.equal (List.length l1) (List.length l2)
@ -255,20 +385,19 @@ suite =
l1 =
events
|> Filter.run
( Filter.and
( Filter.allSendersExcept senders )
( Filter.allTypesExcept types )
(Filter.and
(Filter.allSendersExcept senders)
(Filter.allTypesExcept types)
)
l2 : List Event.Event
l2 =
( List.filter
List.filter
(\e ->
(not <| List.member e.sender senders) &&
(not <| List.member e.eventType types)
(not <| List.member e.sender senders)
&& (not <| List.member e.eventType types)
)
events
)
in
Expect.all
[ Expect.equal (List.length l1) (List.length l2)

View File

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