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_nothing_is_set_in_the_scopes() {
    CanaryAnalysisExecutionRequest request =
        CanaryAnalysisExecutionRequest.builder()
            .scopes(ImmutableList.of(CanaryAnalysisExecutionRequestScope.builder().build()))
            .build();
    Duration intervalDuration = Duration.ofMinutes(3);

    for (int i = 1; i < 6; i++) {
      SetupAndExecuteCanariesStage.ScopeTimeConfig conf =
          stage.calculateStartAndEndForJudgement(request, i, intervalDuration);
      assertEquals(now, conf.getStart());
      assertEquals(now.plus(i * 3, ChronoUnit.MINUTES), conf.getEnd());
    }
  }