Skip to content

Commit 0e2931b

Browse files
Fix DownloadFilter interface to properly return DownloadFilter type
- Override Predicate default methods to return DownloadFilter - Add comprehensive parameterized tests
1 parent 683cff8 commit 0e2931b

File tree

3 files changed

+189
-2
lines changed

3 files changed

+189
-2
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "S3 Transfer Manager",
4+
"contributor": "jencymaryjoseph",
5+
"description": "DownloadFilter type incompatability methods overriden from extended interface"
6+
}

services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/config/DownloadFilter.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,32 @@ public interface DownloadFilter extends Predicate<S3Object> {
3939
boolean test(S3Object s3Object);
4040

4141
/**
42-
* A {@link DownloadFilter} that downloads all non-folder objects. A folder is a 0-byte object created when a customer
43-
* uses S3 console to create a folder, and it always ends with "/".
42+
* Returns a composed filter that performs AND operation
43+
*/
44+
@Override
45+
default DownloadFilter and(Predicate<? super S3Object> other) {
46+
return s3Object -> test(s3Object) && other.test(s3Object);
47+
}
48+
49+
/**
50+
* Returns a composed filter that performs OR operation
51+
*/
52+
@Override
53+
default DownloadFilter or(Predicate<? super S3Object> other) {
54+
return s3Object -> test(s3Object) || other.test(s3Object);
55+
}
56+
57+
/**
58+
* Returns a filter that negates the result
59+
*/
60+
@Override
61+
default DownloadFilter negate() {
62+
return s3Object -> !test(s3Object);
63+
}
64+
65+
/**
66+
* A {@link DownloadFilter} that downloads all non-folder objects. A folder is a 0-byte object created when a customer uses S3
67+
* console to create a folder, and it always ends with "/".
4468
*
4569
* <p>
4670
* This is the default behavior if no filter is provided.

services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/config/DownloadFilterTest.java

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.assertj.core.api.Assertions.assertThat;
1919

2020
import java.util.stream.Stream;
21+
import org.junit.jupiter.api.DisplayName;
2122
import org.junit.jupiter.params.ParameterizedTest;
2223
import org.junit.jupiter.params.provider.Arguments;
2324
import org.junit.jupiter.params.provider.MethodSource;
@@ -38,4 +39,160 @@ public static Stream<Arguments> s3Objects() {
3839
void allObjectsFilter_shouldWork(S3Object s3Object, boolean result) {
3940
assertThat(DownloadFilter.allObjects().test(s3Object)).isEqualTo(result);
4041
}
42+
43+
private static Stream<Arguments> basicOrFilterTestCases() {
44+
return Stream.of(
45+
Arguments.of(
46+
"File in folder1 - should match",
47+
S3Object.builder().key("folder1/test.txt").size(2000L).build(),
48+
true
49+
),
50+
Arguments.of(
51+
"File in folder3 - should match",
52+
S3Object.builder().key("folder3/test.txt").size(2000L).build(),
53+
true
54+
),
55+
Arguments.of(
56+
"File in folder2 - should not match",
57+
S3Object.builder().key("folder2/test.txt").size(2000L).build(),
58+
false
59+
)
60+
);
61+
}
62+
63+
@ParameterizedTest
64+
@MethodSource("basicOrFilterTestCases")
65+
@DisplayName("Test OR filter combinations")
66+
void testBasicOrFilter(String testName, S3Object s3Object, boolean result) {
67+
DownloadFilter folder1Filter = s3Obj -> s3Obj.key().startsWith("folder1");
68+
DownloadFilter folder3Filter = s3Obj -> s3Obj.key().startsWith("folder3");
69+
DownloadFilter combinedFilter = folder1Filter.or(folder3Filter);
70+
assertThat(combinedFilter.test(s3Object))
71+
.as(testName)
72+
.isEqualTo(result);
73+
}
74+
75+
private static Stream<Arguments> basicAndFilterTestCases() {
76+
return Stream.of(
77+
Arguments.of(
78+
"Large text file - should match",
79+
S3Object.builder().key("folder1/test.txt").size(2000L).build(),
80+
true
81+
),
82+
Arguments.of(
83+
"Small text file - should not match",
84+
S3Object.builder().key("folder1/test.txt").size(500L).build(),
85+
false
86+
),
87+
Arguments.of(
88+
"Large non-text file - should not match",
89+
S3Object.builder().key("folder1/test.pdf").size(2000L).build(),
90+
false
91+
)
92+
);
93+
}
94+
95+
@ParameterizedTest
96+
@MethodSource("basicAndFilterTestCases")
97+
@DisplayName("Test AND filter combinations")
98+
void testBasicAndFilter(String testName, S3Object s3Object, boolean result) {
99+
DownloadFilter txtFilter = s3Obj -> s3Obj.key().endsWith(".txt");
100+
DownloadFilter sizeFilter = s3Obj -> s3Obj.size() > 1000L;
101+
DownloadFilter combinedFilter = txtFilter.and(sizeFilter);
102+
assertThat(combinedFilter.test(s3Object))
103+
.as(testName)
104+
.isEqualTo(result);
105+
}
106+
107+
private static Stream<Arguments> basicNegateFilterTestCases() {
108+
return Stream.of(
109+
Arguments.of(
110+
"File in folder1 - should not match",
111+
"FOLDER",
112+
S3Object.builder().key("folder1/test.txt").size(1000L).build(),
113+
false
114+
),
115+
Arguments.of(
116+
"File not in folder1 - should match",
117+
"FOLDER",
118+
S3Object.builder().key("folder2/test.txt").size(1000L).build(),
119+
true
120+
),
121+
Arguments.of(
122+
"Large file - should not match",
123+
"SIZE",
124+
S3Object.builder().key("test.txt").size(2000L).build(),
125+
false
126+
),
127+
Arguments.of(
128+
"Small file - should match",
129+
"SIZE",
130+
S3Object.builder().key("test.txt").size(500L).build(),
131+
true
132+
)
133+
);
134+
}
135+
136+
@ParameterizedTest
137+
@MethodSource("basicNegateFilterTestCases")
138+
@DisplayName("Test NEGATE filter operations")
139+
void testBasicNegateFilter(String testName, String filterType, S3Object s3Object, boolean result) {
140+
DownloadFilter baseFilter;
141+
142+
switch (filterType) {
143+
case "FOLDER":
144+
baseFilter = s3Obj -> s3Obj.key().startsWith("folder1");
145+
break;
146+
case "SIZE":
147+
baseFilter = s3Obj -> s3Obj.size() > 1000L;
148+
break;
149+
default:
150+
throw new IllegalArgumentException("Unknown filter type: " + filterType);
151+
}
152+
153+
DownloadFilter negatedFilter = baseFilter.negate();
154+
assertThat(negatedFilter.test(s3Object))
155+
.as(testName)
156+
.isEqualTo(result);
157+
}
158+
159+
private static Stream<Arguments> combinedFilterTestCases() {
160+
return Stream.of(
161+
Arguments.of(
162+
"Large file in folder1 - should match",
163+
S3Object.builder().key("folder1/test.txt").size(2000L).build(),
164+
true,
165+
"folder1 OR folder3 AND size > 1000"
166+
),
167+
Arguments.of(
168+
"Small file in folder1 - should not match",
169+
S3Object.builder().key("folder1/test.txt").size(500L).build(),
170+
false,
171+
"folder1 OR folder3 AND size > 1000"
172+
),
173+
Arguments.of(
174+
"Large file in folder2 - should not match",
175+
S3Object.builder().key("folder2/test.txt").size(2000L).build(),
176+
false,
177+
"folder1 OR folder3 AND size > 1000"
178+
)
179+
);
180+
}
181+
182+
@ParameterizedTest
183+
@MethodSource("combinedFilterTestCases")
184+
@DisplayName("Test combined filter operations")
185+
void testCombinedFilters(String testName, S3Object s3Object, boolean result, String description) {
186+
DownloadFilter folder1Filter = s3Obj -> s3Obj.key().startsWith("folder1");
187+
DownloadFilter folder3Filter = s3Obj -> s3Obj.key().startsWith("folder3");
188+
DownloadFilter sizeFilter = s3Obj -> s3Obj.size() > 1000L;
189+
190+
DownloadFilter chainedFilter = folder1Filter
191+
.or(folder3Filter)
192+
.and(sizeFilter);
193+
194+
assertThat(chainedFilter.test(s3Object))
195+
.as("%s: %s", testName, description)
196+
.isEqualTo(result);
197+
}
41198
}

0 commit comments

Comments
 (0)