@Override
  public void addAssertions(TestCase test) {
    if (!(test instanceof StructuredTestCase))
      throw new IllegalArgumentException("Expecting StructuredTestCase");

    StructuredTestCase structuredTest = (StructuredTestCase) test;

    Set<String> targetMethods = structuredTest.getTargetMethods();

    List<Mutation> mutants = MutationPool.getMutants();

    ExecutionResult origResult = runTest(test);
    Map<Mutation, ExecutionResult> mutationResults = new HashMap<Mutation, ExecutionResult>();

    // execute on all mutants in the target method that were touched
    for (Mutation mutant : mutants) {
      if (!origResult.getTrace().wasMutationTouched(mutant.getId())) continue;
      if (!targetMethods.contains(mutant.getMethodName())) {
        continue;
      }

      ExecutionResult mutationResult = runTest(test, mutant);
      mutationResults.put(mutant, mutationResult);
    }

    addAssertions(structuredTest, origResult, mutationResults);
  }
  /**
   * Add all assertions to the test case
   *
   * @param test
   * @param origResult
   * @param mutantResult
   * @return
   */
  private int addAssertions(
      StructuredTestCase test,
      ExecutionResult origResult,
      Map<Mutation, ExecutionResult> mutationResults) {
    int numKilled = 0;

    for (Class<?> observerClass : observerClasses) {
      if (origResult.getTrace(observerClass) == null) continue;

      for (int i = 0; i < test.size(); i++) {
        if (test.isExerciseStatement(i))
          origResult.getTrace(observerClass).getAllAssertions(test, i);
      }
    }

    minimizeAssertions(test, origResult, mutationResults);

    return numKilled;
  }
  private void minimizeAssertions(
      StructuredTestCase test,
      ExecutionResult origResult,
      Map<Mutation, ExecutionResult> mutationResults) {
    Set<Integer> killedMutants = new HashSet<Integer>();

    for (int position = test.size() - 1; position >= test.getFirstExerciseStatement(); position--) {
      Statement statement = test.getStatement(position);
      if (!statement.hasAssertions()) continue;

      List<Assertion> assertions = new ArrayList<Assertion>(statement.getAssertions());
      Map<Integer, Set<Integer>> killMap = getKillMap(assertions, mutationResults);
      int num = 0;

      // This is to make sure we prefer assertions on return values.
      // TODO: Refactor
      for (Assertion assertion : assertions) {
        if (assertion instanceof PrimitiveAssertion) {
          boolean killsNew = false;
          for (Integer mutationId : killMap.get(num)) {
            if (!killedMutants.contains(mutationId)) {
              killsNew = true;
              break;
            }
          }
          if (!killsNew) {
            statement.removeAssertion(assertion);
          } else {
            killedMutants.addAll(killMap.get(num));
          }
        }
        num++;
      }

      for (int i = 0; i < assertions.size(); i++) {
        if (!killMap.containsKey(i)) {
          statement.removeAssertion(assertions.get(i));
          continue;
        }
        Assertion assertion = assertions.get(i);
        boolean killsNew = false;
        for (Integer mutationId : killMap.get(i)) {
          if (!killedMutants.contains(mutationId)) {
            killsNew = true;
            break;
          }
        }
        if (!killsNew) {
          statement.removeAssertion(assertion);
        } else {
          killedMutants.addAll(killMap.get(i));
        }
      }

      // If we have no assertions, then add...something?
      if (!statement.hasAssertions()) {
        boolean addedPrimitive = false;
        for (Assertion assertion : assertions) {
          if (assertion instanceof PrimitiveAssertion) {
            statement.addAssertion(assertion);
            addedPrimitive = true;
          }
        }
        if (!addedPrimitive) statement.addAssertion(Randomness.choice(assertions));
      }
    }
  }