public void assertWorkingScoreFromScratch(Score workingScore, Object completedAction) {
   ScoreDirectorFactory assertionScoreDirectorFactory =
       scoreDirectorFactory.getAssertionScoreDirectorFactory();
   if (assertionScoreDirectorFactory == null) {
     assertionScoreDirectorFactory = scoreDirectorFactory;
   }
   ScoreDirector uncorruptedScoreDirector = assertionScoreDirectorFactory.buildScoreDirector();
   uncorruptedScoreDirector.setWorkingSolution(workingSolution);
   Score uncorruptedScore = uncorruptedScoreDirector.calculateScore();
   if (!workingScore.equals(uncorruptedScore)) {
     String scoreCorruptionAnalysis = buildScoreCorruptionAnalysis(uncorruptedScoreDirector);
     uncorruptedScoreDirector.dispose();
     throw new IllegalStateException(
         "Score corruption: the workingScore ("
             + workingScore
             + ") is not the uncorruptedScore ("
             + uncorruptedScore
             + ") after completedAction ("
             + completedAction
             + "):\n"
             + scoreCorruptionAnalysis);
   } else {
     uncorruptedScoreDirector.dispose();
   }
 }
  /**
   * @param uncorruptedScoreDirector never null
   * @return never null
   */
  protected String buildScoreCorruptionAnalysis(ScoreDirector uncorruptedScoreDirector) {
    if (!isConstraintMatchEnabled() || !uncorruptedScoreDirector.isConstraintMatchEnabled()) {
      return "  Score corruption analysis could not be generated because"
          + " either corrupted constraintMatchEnabled ("
          + isConstraintMatchEnabled()
          + ") or uncorrupted constraintMatchEnabled ("
          + uncorruptedScoreDirector.isConstraintMatchEnabled()
          + ") is disabled.\n"
          + "  Check your score constraints manually.";
    }
    Collection<ConstraintMatchTotal> corruptedConstraintMatchTotals = getConstraintMatchTotals();
    Collection<ConstraintMatchTotal> uncorruptedConstraintMatchTotals =
        uncorruptedScoreDirector.getConstraintMatchTotals();

    Map<List<Object>, ConstraintMatch> corruptedMap =
        createConstraintMatchMap(corruptedConstraintMatchTotals);
    Map<List<Object>, ConstraintMatch> excessMap =
        new LinkedHashMap<List<Object>, ConstraintMatch>(corruptedMap);
    Map<List<Object>, ConstraintMatch> missingMap =
        createConstraintMatchMap(uncorruptedConstraintMatchTotals);
    excessMap.keySet().removeAll(missingMap.keySet()); // missingMap == uncorruptedMap
    missingMap.keySet().removeAll(corruptedMap.keySet());

    final int CONSTRAINT_MATCH_DISPLAY_LIMIT = 8;
    StringBuilder analysis = new StringBuilder();
    if (excessMap.isEmpty()) {
      analysis.append(
          "  The corrupted scoreDirector has no ConstraintMatch(s) which are in excess.\n");
    } else {
      analysis
          .append("  The corrupted scoreDirector has ")
          .append(excessMap.size())
          .append(" ConstraintMatch(s) which are in excess (and should not be there):\n");
      int count = 0;
      for (ConstraintMatch constraintMatch : excessMap.values()) {
        if (count >= CONSTRAINT_MATCH_DISPLAY_LIMIT) {
          analysis
              .append("    ... ")
              .append(excessMap.size() - CONSTRAINT_MATCH_DISPLAY_LIMIT)
              .append(" more\n");
          break;
        }
        analysis.append("    ").append(constraintMatch).append("\n");
        count++;
      }
    }
    if (missingMap.isEmpty()) {
      analysis.append(
          "  The corrupted scoreDirector has no ConstraintMatch(s) which are missing.\n");
    } else {
      analysis
          .append("  The corrupted scoreDirector has ")
          .append(missingMap.size())
          .append(" ConstraintMatch(s) which are missing:\n");
      int count = 0;
      for (ConstraintMatch constraintMatch : missingMap.values()) {
        if (count >= CONSTRAINT_MATCH_DISPLAY_LIMIT) {
          analysis
              .append("    ... ")
              .append(missingMap.size() - CONSTRAINT_MATCH_DISPLAY_LIMIT)
              .append(" more\n");
          break;
        }
        analysis.append("    ").append(constraintMatch).append("\n");
        count++;
      }
    }
    if (excessMap.isEmpty() && missingMap.isEmpty()) {
      analysis
          .append(
              "  The corrupted scoreDirector has no ConstraintMatch(s) in excess or missing."
                  + " That could be a bug in this class (")
          .append(getClass())
          .append(").\n");
    }
    appendLegacyConstraintOccurrences(analysis, this, uncorruptedScoreDirector);
    analysis.append("  Check your score constraints.");
    return analysis.toString();
  }