Source:
<code>public void testDeadAgentReaper() throws Exception { final MasterModel masterModel = mock(MasterModel.class); final Clock clock = mock(Clock.class); when(clock.now()).thenReturn(new Instant(2000)); final List<Datapoint> datapoints = Lists.newArrayList( new Datapoint("host1", 0, TIMEOUT_MILLIS - 1, HostStatus.Status.DOWN, true), new Datapoint("host2", 0, TIMEOUT_MILLIS + 1, HostStatus.Status.DOWN, false), new Datapoint("host3", 1000, 1000, HostStatus.Status.UP, false), new Datapoint("host4", 500, 300, HostStatus.Status.DOWN, true), new Datapoint("host5", 5000, 0, HostStatus.Status.DOWN, false), new Datapoint("host6", 0, 0, HostStatus.Status.UP, false) <START> ); <END> when(masterModel.listHosts()).thenReturn(Lists.newArrayList( Iterables.transform(datapoints, new Function<Datapoint, String>() { @Override public String apply(final Datapoint input) { return input.host; } }))); for (final Datapoint datapoint : datapoints) { when(masterModel.getHostStatus(datapoint.host)).thenReturn( HostStatus.newBuilder() .setStatus(datapoint.status) .setAgentInfo(AgentInfo.newBuilder() .setStartTime(datapoint.startTime) .setUptime(datapoint.uptime) .build()) .setJobs(Collections.<JobId, Deployment>emptyMap()) .setStatuses(Collections.<JobId, TaskStatus>emptyMap()) .build()); } final List<String> expected = FluentIterable.from(datapoints) .filter(new Predicate<Datapoint>() { @Override public boolean apply(final Datapoint input) { return input.expectReap; } }) .transform(new Function<Datapoint, String>() { @Override public String apply(final Datapoint input) { return input.host; } }) .toList(); assertThat( DeadAgentReaper.getDeadAgents(masterModel, TIMEOUT_MILLIS, clock), containsInAnyOrder(expected.toArray())); }</code><technical_language>a good fit for a parameterized test :smile:</technical_language>

Target:
public void testDeadAgentReaper() throws Exception { final MasterModel masterModel = mock(MasterModel.class); final Clock clock = mock(Clock.class); when(clock.now()).thenReturn(new Instant(HOURS.toMillis(2000))); final List<Datapoint> datapoints = Lists.newArrayList( new Datapoint("host1", 0, TIMEOUT_HOURS - 1, HostStatus.Status.DOWN, true), new Datapoint("host2", 0, TIMEOUT_HOURS + 1, HostStatus.Status.DOWN, false), new Datapoint("host3", 1000, 1000, HostStatus.Status.UP, false), new Datapoint("host4", 500, 300, HostStatus.Status.DOWN, true), new Datapoint("host5", 5000, 0, HostStatus.Status.DOWN, false), new Datapoint("host6", 0, 0, HostStatus.Status.UP, false) ); when(masterModel.listHosts()).thenReturn(Lists.newArrayList( Iterables.transform(datapoints, new Function<Datapoint, String>() { @Override public String apply(final Datapoint input) { return input.host; } }))); for (final Datapoint datapoint : datapoints) { when(masterModel.getHostStatus(datapoint.host)).thenReturn( HostStatus.newBuilder() .setStatus(datapoint.status) .setAgentInfo(AgentInfo.newBuilder() .setStartTime(datapoint.startTime) .setUptime(datapoint.uptime) .build()) .setJobs(Collections.<JobId, Deployment>emptyMap()) .setStatuses(Collections.<JobId, TaskStatus>emptyMap()) .build()); } final DeadAgentReaper reaper = new DeadAgentReaper( masterModel, TIMEOUT_HOURS, clock); reaper.startAsync().awaitRunning(); verify(masterModel, timeout(5000).atLeastOnce()).listHosts(); for (final Datapoint datapoint : datapoints) { if (datapoint.expectReap) { verify(masterModel, timeout(500)).deregisterHost(datapoint.host); } } }