Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@

import java.util.Objects;

import io.split.android.grammar.Treatments;

/**
* Represents the fallback treatment, with an optional config and a fixed label.
*/
public final class FallbackTreatment {

public static final String LABEL = "fallback treatment";
private static final String LABEL_PREFIX = "fallback - ";

/**
* Default fallback representing "control" treatment with no config.
*/
public static final FallbackTreatment CONTROL = new FallbackTreatment(Treatments.CONTROL);

@NonNull
private final String mTreatment;
Expand All @@ -35,8 +42,8 @@ public String getConfig() {
return mConfig;
}

public String getLabel() {
return LABEL;
public String getLabelPrefix() {
return LABEL_PREFIX;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.split.android.client.fallback;

import androidx.annotation.NonNull;

/**
* Resolves a fallback treatment for a given flag name.
* Returns null if no fallback applies (caller should use control).
*/
public interface FallbackTreatmentsCalculator {

/**
* Resolve a fallback for a given flag name.
*
* @param flagName non-null flag name
* @return a fallback treatment if configured, otherwise "control"
*/
@NonNull
FallbackTreatment resolve(@NonNull String flagName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.split.android.client.fallback;

import androidx.annotation.NonNull;

import java.util.Map;

public final class FallbackTreatmentsCalculatorImpl implements FallbackTreatmentsCalculator {

@NonNull
private final FallbackConfiguration mConfig;

public FallbackTreatmentsCalculatorImpl(@NonNull FallbackConfiguration config) {
mConfig = config;
}

@NonNull
@Override
public FallbackTreatment resolve(@NonNull String flagName) {
Map<String, FallbackTreatment> byFlag = mConfig.getByFlag();
if (byFlag != null) {
FallbackTreatment flagTreatment = byFlag.get(flagName);
if (flagTreatment != null) {
return flagTreatment;
}
}
FallbackTreatment global = mConfig.getGlobal();
if (global != null) {
return global;
}
return FallbackTreatment.CONTROL;
}
}
18 changes: 0 additions & 18 deletions src/main/java/io/split/android/grammar/Treatments.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,7 @@
public class Treatments {

public static final String CONTROL = "control";

/**
* OFF is a synonym for CONTROL.
*/
public static final String OFF = "off";
public static final String ON = "on";

public static boolean isControl(String treatment) {
return CONTROL.equals(treatment) || OFF.equals(treatment);
}

public static String controlSynonym(String treatment) {
if (!isControl(treatment)) {
throw new IllegalArgumentException("Not a control treatment: " + treatment);
}
if (Treatments.OFF.equals(treatment)) {
return Treatments.CONTROL;
}
return Treatments.OFF;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,30 @@

public class FallbackTreatmentTest {

private static final String FALLBACK_TREATMENT = "fallback treatment";
private static final String FALLBACK_TREATMENT = "fallback - ";

@Test
public void constructorSetsFields() {
FallbackTreatment ft = new FallbackTreatment("off", "{\"k\":true}");
assertEquals("off", ft.getTreatment());
assertEquals("{\"k\":true}", ft.getConfig());
assertEquals(FALLBACK_TREATMENT, ft.getLabel());
assertEquals(FALLBACK_TREATMENT, ft.getLabelPrefix());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this method private/ just for testing?

}

@Test
public void configCanBeNull() {
FallbackTreatment ft = new FallbackTreatment("off", null);
assertEquals("off", ft.getTreatment());
assertNull(ft.getConfig());
assertEquals(FALLBACK_TREATMENT, ft.getLabel());
assertEquals(FALLBACK_TREATMENT, ft.getLabelPrefix());
}

@Test
public void convenienceConstructorSetsNullConfig() {
FallbackTreatment ft = new FallbackTreatment("off");
assertEquals("off", ft.getTreatment());
assertNull(ft.getConfig());
assertEquals(FALLBACK_TREATMENT, ft.getLabel());
assertEquals(FALLBACK_TREATMENT, ft.getLabelPrefix());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package io.split.android.client.fallback;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.junit.Test;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class FallbackTreatmentsCalculatorTest {

@Test
public void flagLevelOverrideTakesPrecedence() {
FallbackTreatment global = new FallbackTreatment("off", "{\"g\":true}");
FallbackTreatment byFlag = new FallbackTreatment("on", "{\"f\":true}");
Map<String, FallbackTreatment> map = new HashMap<>();
map.put("my_flag", byFlag);
FallbackConfiguration config = FallbackConfiguration.builder()
.global(global)
.byFlag(map)
.build();

FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
FallbackTreatment resolvedExisting = calculator.resolve("my_flag");
FallbackTreatment resolvedOther = calculator.resolve("other_flag");

assertNotNull(resolvedExisting);
assertEquals(byFlag, resolvedExisting);
assertNotNull(resolvedOther);
assertEquals(global, resolvedOther);
}

@Test
public void globalFallbackIsReturnedWhenNoFlagOverride() {
FallbackTreatment global = new FallbackTreatment("off");
FallbackConfiguration config = FallbackConfiguration.builder()
.global(global)
.byFlag(Collections.emptyMap())
.build();

FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
FallbackTreatment resolved = calculator.resolve("any_flag");

assertNotNull(resolved);
assertEquals(global, resolved);
}

@Test
public void flagLevelFallbackIsReturnedWhenConfigured() {
FallbackTreatment byFlag = new FallbackTreatment("on");
Map<String, FallbackTreatment> map = new HashMap<>();
map.put("flagA", byFlag);
FallbackConfiguration config = FallbackConfiguration.builder()
.byFlag(map)
.build();

FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
FallbackTreatment resolved = calculator.resolve("flagA");

assertNotNull(resolved);
assertEquals(byFlag, resolved);
}

@Test
public void returnsControlWhenNoFallbackConfigured() {
FallbackConfiguration config = FallbackConfiguration.builder()
.build();

FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
FallbackTreatment resolved = calculator.resolve("nope");

assertNotNull(resolved);
assertEquals(FallbackTreatment.CONTROL, resolved);
}

@Test
public void nonexistentFlagFallsBackToGlobal() {
FallbackTreatment global = new FallbackTreatment("off");
Map<String, FallbackTreatment> map = new HashMap<>();
map.put("flagA", new FallbackTreatment("on"));
FallbackConfiguration config = FallbackConfiguration.builder()
.global(global)
.byFlag(map)
.build();

FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
FallbackTreatment resolved = calculator.resolve("flagB");

assertNotNull(resolved);
assertEquals(global, resolved);
}
}
Loading