• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.net.httpflags;
6 
7 import static com.google.common.truth.Truth.assertThat;
8 
9 import static org.junit.Assert.assertThrows;
10 
11 import androidx.test.ext.junit.runners.AndroidJUnit4;
12 import androidx.test.filters.SmallTest;
13 
14 import com.google.common.truth.Correspondence;
15 import com.google.protobuf.ByteString;
16 
17 import org.junit.Test;
18 import org.junit.runner.RunWith;
19 
20 import org.chromium.base.test.util.Batch;
21 
22 import java.util.Map;
23 
24 /** Tests {@link ResolvedFlags} */
25 @Batch(Batch.UNIT_TESTS)
26 @RunWith(AndroidJUnit4.class)
27 public final class ResolvedFlagsTest {
28     private static final Correspondence<ResolvedFlags.Value, String> FLAG_STRING_VALUE_EQUALS =
29             Correspondence.transforming(
30                     ResolvedFlags.Value::getStringValue, "has a string value of");
31 
stringConstrainedValue(String value)32     private static FlagValue.ConstrainedValue.Builder stringConstrainedValue(String value) {
33         return FlagValue.ConstrainedValue.newBuilder().setStringValue(value);
34     }
35 
singleFlag(String flagName, FlagValue.Builder flagValue)36     private static Flags singleFlag(String flagName, FlagValue.Builder flagValue) {
37         return Flags.newBuilder().putFlags(flagName, flagValue.build()).build();
38     }
39 
40     @Test
41     @SmallTest
testResolve_emptyOnEmptyProto()42     public void testResolve_emptyOnEmptyProto() {
43         assertThat(
44                         ResolvedFlags.resolve(Flags.newBuilder().build(), "test_app_id", "1.2.3.4")
45                                 .flags())
46                 .isEmpty();
47     }
48 
49     @Test
50     @SmallTest
testResolve_returnsAllFlags()51     public void testResolve_returnsAllFlags() {
52         assertThat(
53                         ResolvedFlags.resolve(
54                                         Flags.newBuilder()
55                                                 .putFlags(
56                                                         "test_flag_1",
57                                                         FlagValue.newBuilder()
58                                                                 .addConstrainedValues(
59                                                                         stringConstrainedValue(
60                                                                                 "test_flag_1_value"))
61                                                                 .build())
62                                                 .putFlags(
63                                                         "test_flag_2",
64                                                         FlagValue.newBuilder()
65                                                                 .addConstrainedValues(
66                                                                         stringConstrainedValue(
67                                                                                 "test_flag_2_value"))
68                                                                 .build())
69                                                 .build(),
70                                         "test_app_id",
71                                         "1.2.3.4")
72                                 .flags())
73                 .comparingValuesUsing(FLAG_STRING_VALUE_EQUALS)
74                 .containsExactly(
75                         "test_flag_1", "test_flag_1_value", "test_flag_2", "test_flag_2_value");
76     }
77 
78     @Test
79     @SmallTest
testResolve_doesNotReturnFlagWithEmptyConstrainedValue()80     public void testResolve_doesNotReturnFlagWithEmptyConstrainedValue() {
81         assertThat(
82                         ResolvedFlags.resolve(
83                                         singleFlag(
84                                                 "test_flag",
85                                                 FlagValue.newBuilder()
86                                                         .addConstrainedValues(
87                                                                 FlagValue.ConstrainedValue
88                                                                         .newBuilder())),
89                                         "test_app_id",
90                                         "1.2.3.4")
91                                 .flags())
92                 .isEmpty();
93     }
94 
95     @Test
96     @SmallTest
testResolve_doesNotReturnFlagWithNoConstrainedValues()97     public void testResolve_doesNotReturnFlagWithNoConstrainedValues() {
98         assertThat(
99                         ResolvedFlags.resolve(
100                                         singleFlag("test_flag", FlagValue.newBuilder()),
101                                         "test_app_id",
102                                         "1.2.3.4")
103                                 .flags())
104                 .isEmpty();
105     }
106 
107     @Test
108     @SmallTest
testResolve_returnsFlagThatMatchesAppId()109     public void testResolve_returnsFlagThatMatchesAppId() {
110         assertThat(
111                         ResolvedFlags.resolve(
112                                         singleFlag(
113                                                 "test_flag",
114                                                 FlagValue.newBuilder()
115                                                         .addConstrainedValues(
116                                                                 stringConstrainedValue(
117                                                                                 "test_flag_value")
118                                                                         .setAppId("test_app_id"))),
119                                         "test_app_id",
120                                         "1.2.3.4")
121                                 .flags())
122                 .comparingValuesUsing(FLAG_STRING_VALUE_EQUALS)
123                 .containsExactly("test_flag", "test_flag_value");
124     }
125 
126     @Test
127     @SmallTest
testResolve_doesNotReturnFlagThatDoesNotMatchAppId()128     public void testResolve_doesNotReturnFlagThatDoesNotMatchAppId() {
129         assertThat(
130                         ResolvedFlags.resolve(
131                                         singleFlag(
132                                                 "test_flag",
133                                                 FlagValue.newBuilder()
134                                                         .addConstrainedValues(
135                                                                 stringConstrainedValue(
136                                                                                 "test_flag_value")
137                                                                         .setAppId(
138                                                                                 "nonmatching_app_id"))),
139                                         "test_app_id",
140                                         "1.2.3.4")
141                                 .flags())
142                 .isEmpty();
143     }
144 
145     @Test
146     @SmallTest
testResolve_throwsOnEmptyCronetVersion()147     public void testResolve_throwsOnEmptyCronetVersion() {
148         assertThrows(
149                 IllegalArgumentException.class,
150                 () -> {
151                     ResolvedFlags.resolve(Flags.newBuilder().build(), "test_app_id", "");
152                 });
153     }
154 
155     @Test
156     @SmallTest
testResolve_throwsOnInvalidCronetVersion()157     public void testResolve_throwsOnInvalidCronetVersion() {
158         assertThrows(
159                 IllegalArgumentException.class,
160                 () -> {
161                     ResolvedFlags.resolve(Flags.newBuilder().build(), "test_app_id", "1.2.a.4");
162                 });
163     }
164 
165     @Test
166     @SmallTest
testResolve_throwsOnEmptyMinVersion()167     public void testResolve_throwsOnEmptyMinVersion() {
168         Flags flags =
169                 singleFlag(
170                         "test_flag",
171                         FlagValue.newBuilder()
172                                 .addConstrainedValues(
173                                         stringConstrainedValue("test_flag_value")
174                                                 .setMinVersion("")));
175         assertThrows(
176                 IllegalArgumentException.class,
177                 () -> {
178                     ResolvedFlags.resolve(flags, "test_app_id", "1.2.3.4");
179                 });
180     }
181 
182     @Test
183     @SmallTest
testResolve_throwsOnInvalidMinVersion()184     public void testResolve_throwsOnInvalidMinVersion() {
185         Flags flags =
186                 singleFlag(
187                         "test_flag",
188                         FlagValue.newBuilder()
189                                 .addConstrainedValues(
190                                         stringConstrainedValue("test_flag_value")
191                                                 .setMinVersion("1.2.a.4")));
192         assertThrows(
193                 IllegalArgumentException.class,
194                 () -> {
195                     ResolvedFlags.resolve(flags, "test_app_id", "1.2.3.4");
196                 });
197     }
198 
checkMinVersion(String cronetVersion, String minVersion, boolean expectMatch)199     private void checkMinVersion(String cronetVersion, String minVersion, boolean expectMatch) {
200         Map<String, ResolvedFlags.Value> flags =
201                 ResolvedFlags.resolve(
202                                 singleFlag(
203                                         "test_flag",
204                                         FlagValue.newBuilder()
205                                                 .addConstrainedValues(
206                                                         stringConstrainedValue("test_flag_value")
207                                                                 .setMinVersion(minVersion))),
208                                 "test_app_id",
209                                 cronetVersion)
210                         .flags();
211         if (expectMatch) {
212             assertThat(flags)
213                     .comparingValuesUsing(FLAG_STRING_VALUE_EQUALS)
214                     .containsExactly("test_flag", "test_flag_value");
215         } else {
216             assertThat(flags).isEmpty();
217         }
218     }
219 
220     @Test
221     @SmallTest
testResolve_returnsFlagIfCronetVersionIsSameAsMinVersion()222     public void testResolve_returnsFlagIfCronetVersionIsSameAsMinVersion() {
223         checkMinVersion("5.6.7.8", "5.6.7.8", true);
224     }
225 
226     @Test
227     @SmallTest
testResolve_returnsFlagIfCronetPatchVersionIsHigherThanMinPatchVersion()228     public void testResolve_returnsFlagIfCronetPatchVersionIsHigherThanMinPatchVersion() {
229         checkMinVersion("5.6.7.9", "5.6.7.8", true);
230     }
231 
232     @Test
233     @SmallTest
testResolve_doesNotReturnFlagIfCronetPatchVersionIsLowerThanMinPatchVersion()234     public void testResolve_doesNotReturnFlagIfCronetPatchVersionIsLowerThanMinPatchVersion() {
235         checkMinVersion("5.6.7.7", "5.6.7.8", false);
236     }
237 
238     @Test
239     @SmallTest
testResolve_returnsFlagIfCronetMajorVersionIsHigherThanMinMajorVersion()240     public void testResolve_returnsFlagIfCronetMajorVersionIsHigherThanMinMajorVersion() {
241         checkMinVersion("6.6.7.8", "5.6.7.8", true);
242     }
243 
244     @Test
245     @SmallTest
testResolve_doesNotReturnFlagIfCronetPatchVersionIsLowerThanMinMajorVersion()246     public void testResolve_doesNotReturnFlagIfCronetPatchVersionIsLowerThanMinMajorVersion() {
247         checkMinVersion("4.6.7.7", "5.6.7.8", false);
248     }
249 
250     @Test
251     @SmallTest
testResolve_returnsFlagOnCronetMajorVersionMatch()252     public void testResolve_returnsFlagOnCronetMajorVersionMatch() {
253         checkMinVersion("5.0.0.0", "5", true);
254     }
255 
256     @Test
257     @SmallTest
testResolve_doesNotReturnFlagOnLowerCronetMajorVersion()258     public void testResolve_doesNotReturnFlagOnLowerCronetMajorVersion() {
259         checkMinVersion("4.9.9.9", "5", false);
260     }
261 
262     @Test
263     @SmallTest
testResolve_returnsOnlyMatchingConstrainedValue()264     public void testResolve_returnsOnlyMatchingConstrainedValue() {
265         FlagValue.ConstrainedValue matching_value =
266                 stringConstrainedValue("matching_test_flag_value").setAppId("test_app_id").build();
267         FlagValue.ConstrainedValue nonmatching_value =
268                 stringConstrainedValue("nonmatching_test_flag_value")
269                         .setAppId("nonmatching_app_id")
270                         .build();
271 
272         assertThat(
273                         ResolvedFlags.resolve(
274                                         singleFlag(
275                                                 "test_flag",
276                                                 FlagValue.newBuilder()
277                                                         .addConstrainedValues(matching_value)
278                                                         .addConstrainedValues(nonmatching_value)),
279                                         "test_app_id",
280                                         "1.2.3.4")
281                                 .flags())
282                 .comparingValuesUsing(FLAG_STRING_VALUE_EQUALS)
283                 .containsExactly("test_flag", "matching_test_flag_value");
284         assertThat(
285                         ResolvedFlags.resolve(
286                                         singleFlag(
287                                                 "test_flag",
288                                                 FlagValue.newBuilder()
289                                                         .addConstrainedValues(nonmatching_value)
290                                                         .addConstrainedValues(matching_value)),
291                                         "test_app_id",
292                                         "1.2.3.4")
293                                 .flags())
294                 .comparingValuesUsing(FLAG_STRING_VALUE_EQUALS)
295                 .containsExactly("test_flag", "matching_test_flag_value");
296     }
297 
298     @Test
299     @SmallTest
testResolve_returnsFirstMatchingConstrainedValue()300     public void testResolve_returnsFirstMatchingConstrainedValue() {
301         assertThat(
302                         ResolvedFlags.resolve(
303                                         singleFlag(
304                                                 "test_flag",
305                                                 FlagValue.newBuilder()
306                                                         .addConstrainedValues(
307                                                                 stringConstrainedValue(
308                                                                         "test_flag_value_1"))
309                                                         .addConstrainedValues(
310                                                                 stringConstrainedValue(
311                                                                         "test_flag_value_2"))),
312                                         "test_app_id",
313                                         "1.2.3.4")
314                                 .flags())
315                 .comparingValuesUsing(FLAG_STRING_VALUE_EQUALS)
316                 .containsExactly("test_flag", "test_flag_value_1");
317     }
318 
319     @Test
320     @SmallTest
testResolve_doesNotReturnFlagIfMatchingValueIsEmpty()321     public void testResolve_doesNotReturnFlagIfMatchingValueIsEmpty() {
322         assertThat(
323                         ResolvedFlags.resolve(
324                                         singleFlag(
325                                                 "test_flag",
326                                                 FlagValue.newBuilder()
327                                                         .addConstrainedValues(
328                                                                 FlagValue.ConstrainedValue
329                                                                         .newBuilder())
330                                                         .addConstrainedValues(
331                                                                 stringConstrainedValue(
332                                                                         "test_flag_value_should_be_skipped"))),
333                                         "test_app_id",
334                                         "1.2.3.4")
335                                 .flags())
336                 .isEmpty();
337     }
338 
339     @Test
340     @SmallTest
testResolve_returnsFalseValue()341     public void testResolve_returnsFalseValue() {
342         ResolvedFlags.Value value =
343                 ResolvedFlags.resolve(
344                                 singleFlag(
345                                         "test_flag",
346                                         FlagValue.newBuilder()
347                                                 .addConstrainedValues(
348                                                         FlagValue.ConstrainedValue.newBuilder()
349                                                                 .setBoolValue(false))),
350                                 "test_app_id",
351                                 "1.2.3.4")
352                         .flags()
353                         .get("test_flag");
354         assertThat(value).isNotNull();
355         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.BOOL);
356         assertThat(value.getBoolValue()).isFalse();
357     }
358 
359     @Test
360     @SmallTest
testResolve_returnsTrueValue()361     public void testResolve_returnsTrueValue() {
362         ResolvedFlags.Value value =
363                 ResolvedFlags.resolve(
364                                 singleFlag(
365                                         "test_flag",
366                                         FlagValue.newBuilder()
367                                                 .addConstrainedValues(
368                                                         FlagValue.ConstrainedValue.newBuilder()
369                                                                 .setBoolValue(true))),
370                                 "test_app_id",
371                                 "1.2.3.4")
372                         .flags()
373                         .get("test_flag");
374         assertThat(value).isNotNull();
375         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.BOOL);
376         assertThat(value.getBoolValue()).isTrue();
377     }
378 
379     @Test
380     @SmallTest
testResolve_returnsZeroIntValue()381     public void testResolve_returnsZeroIntValue() {
382         ResolvedFlags.Value value =
383                 ResolvedFlags.resolve(
384                                 singleFlag(
385                                         "test_flag",
386                                         FlagValue.newBuilder()
387                                                 .addConstrainedValues(
388                                                         FlagValue.ConstrainedValue.newBuilder()
389                                                                 .setIntValue(0))),
390                                 "test_app_id",
391                                 "1.2.3.4")
392                         .flags()
393                         .get("test_flag");
394         assertThat(value).isNotNull();
395         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.INT);
396         assertThat(value.getIntValue()).isEqualTo(0);
397     }
398 
399     @Test
400     @SmallTest
testResolve_returnsNonZeroIntValue()401     public void testResolve_returnsNonZeroIntValue() {
402         ResolvedFlags.Value value =
403                 ResolvedFlags.resolve(
404                                 singleFlag(
405                                         "test_flag",
406                                         FlagValue.newBuilder()
407                                                 .addConstrainedValues(
408                                                         FlagValue.ConstrainedValue.newBuilder()
409                                                                 .setIntValue(42))),
410                                 "test_app_id",
411                                 "1.2.3.4")
412                         .flags()
413                         .get("test_flag");
414         assertThat(value).isNotNull();
415         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.INT);
416         assertThat(value.getIntValue()).isEqualTo(42);
417     }
418 
419     @Test
420     @SmallTest
testResolve_returnsZeroFloatValue()421     public void testResolve_returnsZeroFloatValue() {
422         ResolvedFlags.Value value =
423                 ResolvedFlags.resolve(
424                                 singleFlag(
425                                         "test_flag",
426                                         FlagValue.newBuilder()
427                                                 .addConstrainedValues(
428                                                         FlagValue.ConstrainedValue.newBuilder()
429                                                                 .setFloatValue(0))),
430                                 "test_app_id",
431                                 "1.2.3.4")
432                         .flags()
433                         .get("test_flag");
434         assertThat(value).isNotNull();
435         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.FLOAT);
436         assertThat(value.getFloatValue()).isEqualTo(0f);
437     }
438 
439     @Test
440     @SmallTest
testResolve_returnsNonZeroFloatValue()441     public void testResolve_returnsNonZeroFloatValue() {
442         ResolvedFlags.Value value =
443                 ResolvedFlags.resolve(
444                                 singleFlag(
445                                         "test_flag",
446                                         FlagValue.newBuilder()
447                                                 .addConstrainedValues(
448                                                         FlagValue.ConstrainedValue.newBuilder()
449                                                                 .setFloatValue(42))),
450                                 "test_app_id",
451                                 "1.2.3.4")
452                         .flags()
453                         .get("test_flag");
454         assertThat(value).isNotNull();
455         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.FLOAT);
456         assertThat(value.getFloatValue()).isEqualTo(42f);
457     }
458 
459     @Test
460     @SmallTest
testResolve_returnsEmptyStringValue()461     public void testResolve_returnsEmptyStringValue() {
462         ResolvedFlags.Value value =
463                 ResolvedFlags.resolve(
464                                 singleFlag(
465                                         "test_flag",
466                                         FlagValue.newBuilder()
467                                                 .addConstrainedValues(
468                                                         FlagValue.ConstrainedValue.newBuilder()
469                                                                 .setStringValue(""))),
470                                 "test_app_id",
471                                 "1.2.3.4")
472                         .flags()
473                         .get("test_flag");
474         assertThat(value).isNotNull();
475         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.STRING);
476         assertThat(value.getStringValue()).isEqualTo("");
477     }
478 
479     @Test
480     @SmallTest
testResolve_returnsNonEmptyStringValue()481     public void testResolve_returnsNonEmptyStringValue() {
482         ResolvedFlags.Value value =
483                 ResolvedFlags.resolve(
484                                 singleFlag(
485                                         "test_flag",
486                                         FlagValue.newBuilder()
487                                                 .addConstrainedValues(
488                                                         FlagValue.ConstrainedValue.newBuilder()
489                                                                 .setStringValue(
490                                                                         "test_string_value"))),
491                                 "test_app_id",
492                                 "1.2.3.4")
493                         .flags()
494                         .get("test_flag");
495         assertThat(value).isNotNull();
496         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.STRING);
497         assertThat(value.getStringValue()).isEqualTo("test_string_value");
498     }
499 
500     @Test
501     @SmallTest
testResolve_returnsEmptyBytesValue()502     public void testResolve_returnsEmptyBytesValue() {
503         ResolvedFlags.Value value =
504                 ResolvedFlags.resolve(
505                                 singleFlag(
506                                         "test_flag",
507                                         FlagValue.newBuilder()
508                                                 .addConstrainedValues(
509                                                         FlagValue.ConstrainedValue.newBuilder()
510                                                                 .setBytesValue(ByteString.EMPTY))),
511                                 "test_app_id",
512                                 "1.2.3.4")
513                         .flags()
514                         .get("test_flag");
515         assertThat(value).isNotNull();
516         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.BYTES);
517         assertThat(value.getBytesValue()).isEqualTo(ByteString.EMPTY);
518     }
519 
520     @Test
521     @SmallTest
testResolve_returnsNonEmptyBytesValue()522     public void testResolve_returnsNonEmptyBytesValue() {
523         ByteString byteString = ByteString.copyFrom(new byte[] {0, 1, 2, 42, -128, 127});
524         ResolvedFlags.Value value =
525                 ResolvedFlags.resolve(
526                                 singleFlag(
527                                         "test_flag",
528                                         FlagValue.newBuilder()
529                                                 .addConstrainedValues(
530                                                         FlagValue.ConstrainedValue.newBuilder()
531                                                                 .setBytesValue(byteString))),
532                                 "test_app_id",
533                                 "1.2.3.4")
534                         .flags()
535                         .get("test_flag");
536         assertThat(value).isNotNull();
537         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.BYTES);
538         assertThat(value.getBytesValue()).isEqualTo(byteString);
539     }
540 
541     @Test
542     @SmallTest
testResolve_throwsOnWrongTypeAccess()543     public void testResolve_throwsOnWrongTypeAccess() {
544         ResolvedFlags.Value value =
545                 ResolvedFlags.resolve(
546                                 singleFlag(
547                                         "test_flag",
548                                         FlagValue.newBuilder()
549                                                 .addConstrainedValues(
550                                                         FlagValue.ConstrainedValue.newBuilder()
551                                                                 .setStringValue("test_string"))),
552                                 "test_app_id",
553                                 "1.2.3.4")
554                         .flags()
555                         .get("test_flag");
556         assertThat(value.getType()).isEqualTo(ResolvedFlags.Value.Type.STRING);
557         assertThrows(
558                 IllegalStateException.class,
559                 () -> {
560                     value.getIntValue();
561                 });
562     }
563 }
564