protected ScopeTimeConfig calculateStartAndEndForJudgement(
      CanaryAnalysisExecutionRequest config, long judgementNumber, Duration judgementDuration) {

    Duration warmupDuration = config.getBeginCanaryAnalysisAfterAsDuration();
    Duration offset = judgementDuration.multipliedBy(judgementNumber);

    ScopeTimeConfig scopeTimeConfig = new ScopeTimeConfig();

    Instant startTime = Optional.ofNullable(config.getStartTime()).orElse(now(clock));
    scopeTimeConfig.start = startTime;
    scopeTimeConfig.end = startTime.plus(offset);

    if (config.getEndTime() == null) {
      scopeTimeConfig.start = scopeTimeConfig.start.plus(warmupDuration);
      scopeTimeConfig.end = scopeTimeConfig.end.plus(warmupDuration);
    }

    // If the look back is defined, use it to recalculate the start time, this is used to do sliding
    // window judgements
    if (config.getLookBackAsInstant().isAfter(ZERO_AS_INSTANT)) {
      scopeTimeConfig.start = scopeTimeConfig.end.minus(config.getLookBackAsDuration());
    }

    return scopeTimeConfig;
  }
@Test
  public void
      test_that_calculateStartAndEndForJudgement_has_expected_start_and_end_when_start_iso_only_is_defined() {
    int interval = 1;
    String startIso = "2018-12-17T20:56:39.689Z";
    Duration lifetimeDuration = Duration.ofMinutes(3L);
    CanaryAnalysisExecutionRequest request =
        CanaryAnalysisExecutionRequest.builder()
            .scopes(
                ImmutableList.of(
                    CanaryAnalysisExecutionRequestScope.builder().startTimeIso(startIso).build()))
            .build();

    SetupAndExecuteCanariesStage.ScopeTimeConfig actual =
        stage.calculateStartAndEndForJudgement(request, interval, lifetimeDuration);
    assertEquals(Instant.parse(startIso), actual.getStart());
    assertEquals(Instant.parse(startIso).plus(3L, ChronoUnit.MINUTES), actual.getEnd());
  }