content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package dexfuzz.listeners;
  
  import dexfuzz.ExecutionResult;
  import dexfuzz.executors.Executor;
  import dexfuzz.Log;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Map;
  
  /**
   * Runs bisection search for divergent programs.
   */
  public class BisectionSearchListener extends BaseListener {
  
    /**
     * Used to remember the seed used to fuzz the fuzzed file, so we can save it with this
     * seed as a name, if we find a divergence.
     */
    private long currentSeed;
  
    /**
     * Used to remember the name of the file we've fuzzed, so we can save it if we
     * find a divergence.
     */
    private String fuzzedFile;
  
    @Override
    public void handleSeed(long seed) {
      currentSeed = seed;
    }
  
    @Override
    public void handleSuccessfullyFuzzedFile(String programName) {
      fuzzedFile = programName;
    }
  
    private void writeToFile(String file, String toWrite) throws IOException {
      PrintWriter writer = new PrintWriter(file);
      writer.write(toWrite);
      writer.close();
    }
  
    private String extractExpectedOutput(ExecutionResult result) {
      StringBuilder builder = new StringBuilder();
      // Skip last, artificial output line with return code.
      for (int i = 0; i < result.output.size() - 1; i++) {
        builder.append(result.output.get(i)).append("\n");
      }
      return builder.toString();
    }
  
    @Override
    public void handleDivergences(Map<String, List<Executor>> outputMap) {
      if (outputMap.size() != 2) {
        // It's unclear which output should be considered reference output.
        return;
      }
      try {
        File expected_output_file = File.createTempFile("expected_output", ".txt");
        String outputFile = String.format("bisection_outputs/%d_out.txt", currentSeed);
        String logFile = String.format("bisection_outputs/%d_log.txt", currentSeed);
        List<List<Executor>> executorsGroupedByOutput =
            new ArrayList<List<Executor>>(outputMap.values());
        List<String> outputs = new ArrayList<String>();
        for (List<Executor> executors : executorsGroupedByOutput) {
          outputs.add(extractExpectedOutput(executors.get(0).getResult()));
        }
        for (int i = 0; i < 2; i++) {
          String output = outputs.get(i);
          String otherOutput = outputs.get(1 - i);
          List<Executor> executors = executorsGroupedByOutput.get(i);
          for (Executor executor : executors) {
            if (executor.isBisectable()) {
              writeToFile(expected_output_file.getAbsolutePath(), otherOutput);
              ExecutionResult result = executor.runBisectionSearch(fuzzedFile,
                  expected_output_file.getAbsolutePath(), logFile);
              writeToFile(outputFile, result.getFlattenedAllWithNewlines());
            }
          }
        }
        expected_output_file.delete();
      } catch (IOException e) {
        Log.error(
            "BisectionSearchListener.handleDivergences() caught an IOException " + e.toString());
      }
    }
  
  }
  