Skip to content

Commit

Permalink
Add prefetch API. Closes #20
Browse files Browse the repository at this point in the history
  • Loading branch information
sddamico committed Nov 2, 2015
1 parent 65ab6eb commit ee49154
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 42 deletions.
14 changes: 14 additions & 0 deletions sixpack-java/src/main/java/com/seatgeek/sixpack/Experiment.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ public ParticipatingExperiment participate() {
return sixpack.participate(this);
}

/**
* Much like the call to {@link #participate()}, this method will call to the sixpack server and
* fetch the alternative to use in the test. Unlike participate though, this will
* <p/>
* If an exception occurs trying to prefetch the alternative, the control alternative will
* be selected and returned.
* <p/>
* This call makes blocking network requests.
*/
public PrefetchedExperiment prefetch() {
return sixpack.prefetch(this);
}

/**
* @return true if this Experiment was created with a forced {@link Alternative} choice
*/
Expand All @@ -70,4 +83,5 @@ public String toString() {
public Alternative getControlAlternative() {
return alternatives.iterator().next();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class ParticipatingExperiment {
/**
* The {@link Sixpack} instance that this {@link ParticipatingExperiment is associated with}
*/
public final Sixpack sixpack;
private final Sixpack sixpack;

/**
* The {@link Experiment} that this {@link ParticipatingExperiment} was created from
Expand Down Expand Up @@ -48,16 +48,23 @@ public boolean equals(Object o) {

if (!baseExperiment.equals(that.baseExperiment)) return false;
if (!selectedAlternative.equals(that.selectedAlternative)) return false;
if (!sixpack.equals(that.sixpack)) return false;

return true;
}

@Override
public int hashCode() {
int result = sixpack.hashCode();
result = 31 * result + baseExperiment.hashCode();
int result = baseExperiment.hashCode();
result = 31 * result + selectedAlternative.hashCode();
return result;
}

@Override
public String toString() {
return "ParticipatingExperiment{" +
"sixpack=" + sixpack +
", baseExperiment=" + baseExperiment +
", selectedAlternative=" + selectedAlternative +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.seatgeek.sixpack;

public class PrefetchedExperiment {

private final Sixpack sixpack;

public final Experiment baseExperiment;

public final Alternative selectedAlternative;

public PrefetchedExperiment(final Sixpack sixpack, final Experiment experiment, final Alternative alternative) {
this.sixpack = sixpack;
baseExperiment = experiment;
selectedAlternative = alternative;
}

public ParticipatingExperiment participate() {
return sixpack.participate(baseExperiment);
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof PrefetchedExperiment)) return false;

final PrefetchedExperiment that = (PrefetchedExperiment) o;

if (baseExperiment != null ? !baseExperiment.equals(that.baseExperiment) : that.baseExperiment != null)
return false;
return !(selectedAlternative != null ? !selectedAlternative.equals(that.selectedAlternative) : that.selectedAlternative != null);
}

@Override
public int hashCode() {
int result = baseExperiment != null ? baseExperiment.hashCode() : 0;
result = 31 * result + (selectedAlternative != null ? selectedAlternative.hashCode() : 0);
return result;
}
}
24 changes: 23 additions & 1 deletion sixpack-java/src/main/java/com/seatgeek/sixpack/Sixpack.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ ParticipatingExperiment participate(final Experiment experiment) {
ParticipateResponse response = api.participate(experiment,
new ArrayList<>(experiment.alternatives),
experiment.forcedChoice,
experiment.trafficFraction
experiment.trafficFraction,
null
);

return new ParticipatingExperiment(Sixpack.this, experiment, response.getSelectedAlternative());
Expand All @@ -143,6 +144,27 @@ ParticipatingExperiment participate(final Experiment experiment) {
}
}

/**
* Internal method used by {@link Experiment} to prefetch a selected alternative
*/
PrefetchedExperiment prefetch(final Experiment experiment) {
logParticipate(experiment);

try {
ParticipateResponse response = api.participate(experiment,
new ArrayList<>(experiment.alternatives),
experiment.forcedChoice,
experiment.trafficFraction,
true
);

return new PrefetchedExperiment(Sixpack.this, experiment, response.getSelectedAlternative());
} catch (RuntimeException e) {
logException(experiment, e);
return new PrefetchedExperiment(Sixpack.this, experiment, experiment.getControlAlternative());
}
}

/**
* Internal method used by {@link ParticipatingExperiment} to indicate that a conversion has occurred
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ ParticipateResponse participate(
@Query("experiment") Experiment experiment,
@Query("alternatives") List<Alternative> alternatives,
@Query("force") Alternative forcedAlternative,
@Query("traffic_fraction") Double trafficFraction
@Query("traffic_fraction") Double trafficFraction,
@Query("prefetch") Boolean prefetch
);

@GET("/convert")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,21 @@ public void testGetAlternatives() {
assertEquals(setOfAlternatives, experimentB.alternatives);
}

@Test
public void testGetControlAlternative() {
String name = "test-experiment";
Alternative one = new Alternative("one");
Alternative two = new Alternative("two");
Alternative three = new Alternative("three");

Experiment experiment = new ExperimentBuilder(mockSixpack)
.withName(name)
.withAlternatives(one, two, three)
.build();

assertEquals(one, experiment.getControlAlternative());
}

@Test(expected = NoAlternativesException.class)
public void testNoAlternativesThrows() {
String name = "test-experiment";
Expand Down Expand Up @@ -201,4 +216,19 @@ public void testParticipateCallsSixpackParticipate() {

verify(mockSixpack).participate(experiment);
}

@Test
public void testPrefetchCallsSixpackPrefetch() {
String name = "test-experiment";
Alternative test = new Alternative("test");

Experiment experiment = new ExperimentBuilder(mockSixpack)
.withName(name)
.withAlternatives(test)
.build();

experiment.prefetch();

verify(mockSixpack).prefetch(experiment);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,6 @@ public void setUp() {
initMocks(this);
}

@Test
public void testGetSixpack() throws Exception {
ParticipatingExperiment participatingExperiment = new ParticipatingExperiment(mockSixpack, mockExperiment, mockAlternative);

Sixpack sixpack = participatingExperiment.sixpack;

assertEquals(sixpack, mockSixpack);
}

@Test
public void testGetBaseExperiment() throws Exception {
ParticipatingExperiment participatingExperiment = new ParticipatingExperiment(mockSixpack, mockExperiment, mockAlternative);

Experiment baseExperiment = participatingExperiment.baseExperiment;

assertEquals(baseExperiment, mockExperiment);
}

@Test
public void testGetAlternative() {
Alternative testAlternative = new Alternative("green");

ParticipatingExperiment participatingExperiment = new ParticipatingExperiment(mockSixpack, mockExperiment, testAlternative);

assertEquals(participatingExperiment.selectedAlternative, testAlternative);
}

@Test
public void testConvertCallsSixpackConvert() {
ParticipatingExperiment participatingExperiment = new ParticipatingExperiment(mockSixpack, mockExperiment, mockAlternative);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.seatgeek.sixpack;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;

import nl.jqno.equalsverifier.EqualsVerifier;
import nl.jqno.equalsverifier.Warning;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;

public class PrefetchedExperimentTest {
@Mock Sixpack mockSixpack;

@Mock Experiment mockExperiment;

@Mock Alternative mockAlternative;

@Before
public void setUp() {
initMocks(this);
}

@Test
public void testParticipateCallsSixpackParticipate() {
PrefetchedExperiment prefetchedExperiment = new PrefetchedExperiment(mockSixpack, mockExperiment, mockAlternative);

prefetchedExperiment.participate();

verify(mockSixpack).participate(mockExperiment);
}

@Test
public void testEquals() {
EqualsVerifier.forClass(PrefetchedExperiment.class).suppress(Warning.NULL_FIELDS, Warning.STRICT_INHERITANCE).verify();
}
}
64 changes: 55 additions & 9 deletions sixpack-java/src/test/java/com/seatgeek/sixpack/SixpackTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,28 +90,74 @@ public void testExperimentNotNull() {
public void testParticipateInSuccess() {
ParticipateResponse response = new ParticipateResponse();
AlternativeName name = new AlternativeName();
name.name = "green";
name.name = "red";
response.alternative = name;

when(mockApi.participate(Matchers.<Experiment>anyObject(), Matchers.<List<Alternative>>anyObject(), Matchers.<Alternative>anyObject(), anyDouble()))
when(mockApi.participate(Matchers.<Experiment>anyObject(), Matchers.<List<Alternative>>anyObject(), Matchers.<Alternative>anyObject(), anyDouble(), Matchers.<Boolean>eq(null)))
.thenReturn(response);

Sixpack sixpack = new Sixpack(mockApi);
Experiment experiment = new Experiment(sixpack, "test-experience", new HashSet<Alternative>(), null, 1.0d);

Alternative greenAlternative = new Alternative("green");
Alternative redAlternative = new Alternative("red");

Experiment experiment = new ExperimentBuilder(sixpack)
.withName("test-experience")
.withAlternatives(greenAlternative, redAlternative)
.build();
ParticipatingExperiment participatingExperiment = sixpack.participate(experiment);

assertEquals(new ParticipatingExperiment(sixpack, experiment, redAlternative), participatingExperiment);
}

@Test
public void testParticipateNetworkFailureReturnsControl() {
when(mockApi.participate(Matchers.<Experiment>anyObject(), Matchers.<List<Alternative>>anyObject(), Matchers.<Alternative>anyObject(), anyDouble(), Matchers.<Boolean>eq(null)))
.thenThrow(RetrofitError.networkError("http://sixpack.seatgeek.com", new IOException()));

Sixpack sixpack = new Sixpack(mockApi);

Alternative greenAlternative = new Alternative("green");
Alternative redAlternative = new Alternative("red");

Experiment experiment = new ExperimentBuilder(sixpack)
.withName("test-experience")
.withAlternatives(greenAlternative, redAlternative)
.build();

ParticipatingExperiment participatingExperiment = sixpack.participate(experiment);

assertEquals(new ParticipatingExperiment(sixpack, experiment, new Alternative("green")), participatingExperiment);
assertEquals(new ParticipatingExperiment(sixpack, experiment, greenAlternative), participatingExperiment);
}

@Test
public void testNetworkFailureReturnsControl() {
public void testPrefetchSuccess() {
ParticipateResponse response = new ParticipateResponse();
AlternativeName name = new AlternativeName();
name.name = "green";
name.name = "red";
response.alternative = name;

when(mockApi.participate(Matchers.<Experiment>anyObject(), Matchers.<List<Alternative>>anyObject(), Matchers.<Alternative>anyObject(), anyDouble()))
when(mockApi.participate(Matchers.<Experiment>anyObject(), Matchers.<List<Alternative>>anyObject(), Matchers.<Alternative>anyObject(), anyDouble(), eq(true)))
.thenReturn(response);

Sixpack sixpack = new Sixpack(mockApi);

Alternative greenAlternative = new Alternative("green");
Alternative redAlternative = new Alternative("red");

Experiment experiment = new ExperimentBuilder(sixpack)
.withName("test-experience")
.withAlternatives(greenAlternative, redAlternative)
.build();

PrefetchedExperiment prefetched = sixpack.prefetch(experiment);

assertEquals(new PrefetchedExperiment(sixpack, experiment, redAlternative), prefetched);
}

@Test
public void testPrefetchNetworkFailureReturnsControl() {
when(mockApi.participate(Matchers.<Experiment>anyObject(), Matchers.<List<Alternative>>anyObject(), Matchers.<Alternative>anyObject(), anyDouble(), eq(true)))
.thenThrow(RetrofitError.networkError("http://sixpack.seatgeek.com", new IOException()));

Sixpack sixpack = new Sixpack(mockApi);
Expand All @@ -124,9 +170,9 @@ public void testNetworkFailureReturnsControl() {
.withAlternatives(greenAlternative, redAlternative)
.build();

ParticipatingExperiment participatingExperiment = sixpack.participate(experiment);
PrefetchedExperiment prefetched = sixpack.prefetch(experiment);

assertEquals(new ParticipatingExperiment(sixpack, experiment, greenAlternative), participatingExperiment);
assertEquals(new PrefetchedExperiment(sixpack, experiment, greenAlternative), prefetched);
}

@Test
Expand Down

0 comments on commit ee49154

Please sign in to comment.