Skip to content

Commit

Permalink
Implement the kotlin AndFilter and OrFilter. (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
wangyaopw authored Sep 1, 2022
1 parent 782797e commit 6dbcda7
Show file tree
Hide file tree
Showing 6 changed files with 339 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2022 The Cross-Media Measurement Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.wfanet.virtualpeople.common.fieldfilter

import com.google.protobuf.Descriptors
import com.google.protobuf.MessageOrBuilder
import org.wfanet.virtualpeople.common.FieldFilterProto

/**
* The implementation of [FieldFilter] when op is AND in config.
*
* Always use [FieldFilter.create]. Users should never construct a [AndFilter] directly.
*/
internal class AndFilter(descriptor: Descriptors.Descriptor, config: FieldFilterProto) :
FieldFilter {

private val subFilters: List<FieldFilter>

init {
if (config.op != FieldFilterProto.Op.AND) {
error("Op must be AND. Input FieldFilterProto: $config")
}
if (config.subFiltersCount == 0) {
error("sub_filters must be set when op is AND. Input FieldFilterProto: $config")
}
subFilters = config.subFiltersList.map { FieldFilter.create(descriptor, it) }
}

/** Returns true if all [subFilters] match */
override fun matches(messageOrBuilder: MessageOrBuilder): Boolean {
for (subFilter in subFilters) {
if (!subFilter.matches(messageOrBuilder)) {
return false
}
}
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ sealed interface FieldFilter {
return when (config.op) {
Op.HAS -> HasFilter(descriptor, config)
Op.EQUAL -> EqualFilter(descriptor, config)
Op.AND -> AndFilter(descriptor, config)
Op.OR -> OrFilter(descriptor, config)
Op.ANY_IN -> AnyInFilter.create(descriptor, config)
Op.GT,
Op.LT,
Op.IN,
Op.REGEXP,
Op.OR,
Op.AND,
Op.NOT,
Op.PARTIAL,
Op.TRUE -> TODO("Not yet implemented")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2022 The Cross-Media Measurement Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.wfanet.virtualpeople.common.fieldfilter

import com.google.protobuf.Descriptors
import com.google.protobuf.MessageOrBuilder
import org.wfanet.virtualpeople.common.FieldFilterProto

/**
* The implementation of [FieldFilter] when op is OR in config.
*
* Always use [FieldFilter.create]. Users should never construct a [OrFilter] directly.
*/
internal class OrFilter(descriptor: Descriptors.Descriptor, config: FieldFilterProto) :
FieldFilter {

private val subFilters: List<FieldFilter>

init {
if (config.op != FieldFilterProto.Op.OR) {
error("Op must be OR. Input FieldFilterProto: $config")
}
if (config.subFiltersCount == 0) {
error("sub_filters must be set when op is AND. Input FieldFilterProto: $config")
}
subFilters = config.subFiltersList.map { FieldFilter.create(descriptor, it) }
}

/** Returns true if any of the [subFilters] matches. */
override fun matches(messageOrBuilder: MessageOrBuilder): Boolean {
for (subFilter in subFilters) {
if (subFilter.matches(messageOrBuilder)) {
return true
}
}
return false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2022 The Cross-Media Measurement Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.wfanet.virtualpeople.common.fieldfilter

import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.wfanet.virtualpeople.common.FieldFilterProto.Op
import org.wfanet.virtualpeople.common.fieldFilterProto
import org.wfanet.virtualpeople.common.test.*

@RunWith(JUnit4::class)
class AndFilterTest {

@Test
fun `no sub filter should fail`() {
val fieldFilter = fieldFilterProto { op = Op.AND }
assertFailsWith<IllegalStateException> {
FieldFilter.create(TestProto.getDescriptor(), fieldFilter)
}
}

@Test
fun `all subfilters match should pass`() {
val fieldFilter = fieldFilterProto {
op = Op.AND
subFilters.add(
fieldFilterProto {
name = "a.b.int32_value"
op = Op.EQUAL
value = "1"
}
)
subFilters.add(
fieldFilterProto {
name = "a.b.int64_value"
op = Op.EQUAL
value = "1"
}
)
}
val filter = FieldFilter.create(TestProto.getDescriptor(), fieldFilter)
val testProto1 = testProto {
a = testProtoA {
b = testProtoB {
int32Value = 1
int64Value = 1
}
}
}
assertTrue(filter.matches(testProto1))
assertTrue(filter.matches(testProto1.toBuilder()))
}

@Test
fun `any subfilter mismatch should fail`() {
val fieldFilter = fieldFilterProto {
op = Op.AND
subFilters.add(
fieldFilterProto {
name = "a.b.int32_value"
op = Op.EQUAL
value = "1"
}
)
subFilters.add(
fieldFilterProto {
name = "a.b.int64_value"
op = Op.EQUAL
value = "1"
}
)
}
val filter = FieldFilter.create(TestProto.getDescriptor(), fieldFilter)
val testProto1 = testProto {
a = testProtoA {
b = testProtoB {
int32Value = 1
int64Value = 2
}
}
}
assertFalse(filter.matches(testProto1))
assertFalse(filter.matches(testProto1.toBuilder()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,33 @@ kt_jvm_test(
"@wfa_common_jvm//imports/kotlin/kotlin/test",
],
)

kt_jvm_test(
name = "AndFilterTest",
srcs = ["AndFilterTest.kt"],
test_class = "org.wfanet.virtualpeople.common.fieldfilter.AndFilterTest",
deps = [
"//src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter",
"//src/main/proto/wfa/virtual_people/common/field_filter/test:test_kt_jvm_proto",
"@wfa_common_jvm//imports/java/com/google/common/truth",
"@wfa_common_jvm//imports/java/com/google/common/truth/extensions/proto",
"@wfa_common_jvm//imports/java/com/google/protobuf/util",
"@wfa_common_jvm//imports/java/org/junit",
"@wfa_common_jvm//imports/kotlin/kotlin/test",
],
)

kt_jvm_test(
name = "OrFilterTest",
srcs = ["OrFilterTest.kt"],
test_class = "org.wfanet.virtualpeople.common.fieldfilter.OrFilterTest",
deps = [
"//src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter",
"//src/main/proto/wfa/virtual_people/common/field_filter/test:test_kt_jvm_proto",
"@wfa_common_jvm//imports/java/com/google/common/truth",
"@wfa_common_jvm//imports/java/com/google/common/truth/extensions/proto",
"@wfa_common_jvm//imports/java/com/google/protobuf/util",
"@wfa_common_jvm//imports/java/org/junit",
"@wfa_common_jvm//imports/kotlin/kotlin/test",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2022 The Cross-Media Measurement Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.wfanet.virtualpeople.common.fieldfilter

import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.wfanet.virtualpeople.common.FieldFilterProto.Op
import org.wfanet.virtualpeople.common.fieldFilterProto
import org.wfanet.virtualpeople.common.test.*

@RunWith(JUnit4::class)
class OrFilterTest {

@Test
fun `no sub filter should fail`() {
val fieldFilter = fieldFilterProto { op = Op.OR }
assertFailsWith<IllegalStateException> {
FieldFilter.create(TestProto.getDescriptor(), fieldFilter)
}
}

@Test
fun `any subfilter matches should pass`() {
val fieldFilter = fieldFilterProto {
op = Op.OR
subFilters.add(
fieldFilterProto {
name = "a.b.int32_value"
op = Op.EQUAL
value = "1"
}
)
subFilters.add(
fieldFilterProto {
name = "a.b.int64_value"
op = Op.EQUAL
value = "1"
}
)
}
val filter = FieldFilter.create(TestProto.getDescriptor(), fieldFilter)

val testProto1 = testProto {
a = testProtoA {
b = testProtoB {
int32Value = 2
int64Value = 1
}
}
}
assertTrue(filter.matches(testProto1))
assertTrue(filter.matches(testProto1.toBuilder()))

val testProto2 = testProto { a = testProtoA { b = testProtoB { int64Value = 1 } } }
assertTrue(filter.matches(testProto1))
assertTrue(filter.matches(testProto1.toBuilder()))
}

@Test
fun `All subfilters mismatch should fail`() {
val fieldFilter = fieldFilterProto {
op = Op.OR
subFilters.add(
fieldFilterProto {
name = "a.b.int32_value"
op = Op.EQUAL
value = "1"
}
)
subFilters.add(
fieldFilterProto {
name = "a.b.int64_value"
op = Op.EQUAL
value = "1"
}
)
}
val filter = FieldFilter.create(TestProto.getDescriptor(), fieldFilter)
val testProto1 = testProto {
a = testProtoA {
b = testProtoB {
int32Value = 2
int64Value = 2
}
}
}
assertFalse(filter.matches(testProto1))
assertFalse(filter.matches(testProto1.toBuilder()))
}
}

0 comments on commit 6dbcda7

Please sign in to comment.