/* Copyright 2014 Google Inc. All rights reserved.
 *
 *  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 com.google.jenkins.flakyTestHandler.plugin;

import com.google.jenkins.flakyTestHandler.junit.FlakyCaseResult;
import com.google.jenkins.flakyTestHandler.junit.FlakyTestResult;
import com.google.jenkins.flakyTestHandler.plugin.HistoryAggregatedFlakyTestResultAction.SingleTestFlakyStatsWithRevision;

import com.thoughtworks.xstream.XStream;

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import hudson.Launcher;
import hudson.XmlFile;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.Run;
import hudson.remoting.VirtualChannel;
import hudson.tasks.junit.TestResult;
import hudson.tasks.test.AbstractTestResultAction;
import hudson.util.HeapSpaceStringConverter;
import hudson.util.XStream2;
import jenkins.model.RunAction2;

/**
 * Action for each test run, to record flaky stats for all the tests
 *
 * @author Qingzhou Luo
 */
public class FlakyTestResultAction implements RunAction2 {

  /**
   * FlakyRunStats object, use WeakReference to reduce memory overhead since it will be
   * stored on the disk
   */
  private transient WeakReference<FlakyRunStats> flakyRunStats;

  private static final XStream XSTREAM = new XStream2();

  /**
   * This build
   */
  private AbstractBuild<?,?> build;

  public static final Logger logger = Logger.getLogger(FlakyTestResultAction.class.getName());

  static {
    XSTREAM.registerConverter(new HeapSpaceStringConverter(),100);
  }

  /**
   * Construct a FlakyTestResultAction object with AbstractBuild and BuildListener
   *
   * @param build this build
   * @param listener listener of this build
   */
  public FlakyTestResultAction(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
    this.build = build;
    // TODO consider the possibility that there is >1 such action
    AbstractTestResultAction action = build.getAction(AbstractTestResultAction.class);
    if (action != null) {
      Object latestResult = action.getResult();
      if (latestResult != null && latestResult instanceof TestResult) {
        VirtualChannel channel = launcher.getChannel();
        if(channel == null) {
          throw new InterruptedException("Could not get channel to run a program remotely.");
        }
        FlakyTestResult flakyTestResult = launcher.getChannel().call(new FlakyTestResultCollector((TestResult) latestResult));

        flakyTestResult.freeze(action, build);
        FlakyRunStats stats = new FlakyRunStats(flakyTestResult.getTestFlakyStatsMap());
        setFlakyRunStats(stats, listener);
      }
    } else {
      logger.log(Level.WARNING, "No test result found, please publish junit report first");
    }
  }

  /**
   * Empty constructor for testing purpose
   */
  // Visible for testing
  FlakyTestResultAction() {

  }

  private XmlFile getDataFile() {
    return new XmlFile(XSTREAM,new File(build.getRootDir(), "junitFlakyStatsResult.xml"));
  }

  /**
   * Loads a {@link TestResult} from disk.
   */
  private FlakyRunStats load() {
    FlakyRunStats stats;
    try {
      stats = (FlakyRunStats)getDataFile().read();
    } catch (IOException e) {
      stats = new FlakyRunStats();   // return a dummy
    }
    return stats;
  }

  @Override
  public void onAttached(Run<?, ?> r) {
    this.build = (AbstractBuild<?,?>) r;
  }

  @Override
  public void onLoad(Run<?, ?> r) {
    this.build = (AbstractBuild<?,?>) r;
  }

  @Override
  public String getIconFileName() {
    return null;
  }

  @Override
  public String getDisplayName() {
    return null;
  }

  @Override
  public String getUrlName() {
    return null;
  }

  public synchronized FlakyRunStats getFlakyRunStats() {
    FlakyRunStats stats;
    if (flakyRunStats == null) {
      stats = load();
      flakyRunStats = new WeakReference<FlakyRunStats>(stats);
    } else {
      stats = flakyRunStats.get();
    }

    if (stats == null) {
      stats = load();
      flakyRunStats = new WeakReference<FlakyRunStats>(stats);
    }
    return stats;
  }

  // Visible for testing
  synchronized void setFlakyRunStats(FlakyRunStats stats) {
    flakyRunStats = new WeakReference<FlakyRunStats>(stats);
  }

  /**
   * Overwrites the {@link FlakyRunStats} by a new data set.
   */
  public synchronized void setFlakyRunStats(FlakyRunStats stats, BuildListener listener) {

    // persist the data
    try {
      getDataFile().write(stats);
    } catch (IOException e) {
      e.printStackTrace(listener.fatalError("Failed to save the JUnit flaky test stats result"));
    }
    this.flakyRunStats = new WeakReference<FlakyRunStats>(stats);
  }

  /**
   * Get display names for all the test cases
   *
   * @param results Collection of {@link hudson.tasks.junit.TestResult} objects
   * @return the set of display names for all the test cases
   */
  public static Set<String> getTestIdFromTestResults(
      Collection<? extends hudson.tasks.test.TestResult> results) {
    Set<String> testIdSet = new HashSet<String>();
    for (hudson.tasks.test.TestResult testResult : results) {
      if (testResult instanceof FlakyCaseResult) {
        testIdSet.add(((FlakyCaseResult)testResult).getFullDisplayName());
      }
    }
    return testIdSet;
  }

  /**
   * Class to hold all the passing/failing/flaky tests for one run
   */
  public static class FlakyRunStats {

    /**
     * Map between test case name and its flaky stats with revision info
     */
    Map<String, SingleTestFlakyStatsWithRevision> testFlakyStatsWithRevisionMap;

    public FlakyRunStats() {
      this.testFlakyStatsWithRevisionMap = new HashMap<String, SingleTestFlakyStatsWithRevision>();
    }

    public FlakyRunStats(Map<String, SingleTestFlakyStatsWithRevision>
        testFlakyStatsWithRevisionMap) {
      this.testFlakyStatsWithRevisionMap = testFlakyStatsWithRevisionMap;
    }

    public Map<String, SingleTestFlakyStatsWithRevision> getTestFlakyStatsWithRevisionMap() {
      return testFlakyStatsWithRevisionMap;
    }

    /**
     * Is current run flaky or not. Build will be marked as unstable if there are flaky tests
     * and no failing test
     *
     * @return true if there is no failing test but there are some flaky tests
     */
    public boolean isFlaked() {
      if (testFlakyStatsWithRevisionMap == null) {
        return false;
      }

      boolean seenFlake = false;
      for (Map.Entry<String, SingleTestFlakyStatsWithRevision>
          singleTestFlakyStatsWithRevisionEntry : testFlakyStatsWithRevisionMap.entrySet()) {
        if (singleTestFlakyStatsWithRevisionEntry.getValue().getStats().isFailed()) {
          return false;
        } else if (singleTestFlakyStatsWithRevisionEntry.getValue().getStats().isFlaked()) {
          seenFlake = true;
        }
      }
      return seenFlake;
    }
  }
}

@@ -3163,37 +3163,23 @@
 esult = 
-launcher.getC
+c
 hannel
-()
 .call(ne

Replace with correct object