/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.testutils;

import alluxio.Constants;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.junit.Rule;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.rules.Timeout;
import org.junit.runner.Description;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Collections;
import java.util.List;

/**
 * Base class used for specifying the maximum time a test should run for.
 */
public abstract class BaseIntegrationTest {
  /** Logger is required so the log file is initialized, otherwise truncate can fail. */
  @SuppressFBWarnings("UUF_UNUSED_FIELD")
  private static final Logger LOG = LoggerFactory.getLogger(BaseIntegrationTest.class);

  @Rule
  public RuleChain mRules = createRuleChain();

  private RuleChain createRuleChain() {
    RuleChain chain = RuleChain.emptyRuleChain();
    chain = chain.around(logHandler());
    for (TestRule rule : rules()) {
      chain = chain.around(rule);
    }
    // Make Timeout the innermost rule so that other rules run in the main thread. Everything inside
    // Timeout is executed in a new thread.
    chain = chain.around(Timeout.millis(Constants.MAX_TEST_DURATION_MS));
    return chain;
  }

  private TestWatcher logHandler() {
    return new TestWatcher() {
      // When tests fail, save the logs.
      protected void failed(Throwable t, Description description) {
        try {
          Files.copy(Paths.get(Constants.TESTS_LOG),
              Paths.get(Constants.TEST_LOG_DIR, String.format("%s-%s.log",
                  description.getClassName(), description.getMethodName())),
              StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
          if (t != null) {
            t.addSuppressed(e);
          } else {
            throw new RuntimeException(e);
          }
        }
        return;
      }

      // Before each test starts, truncate the log file.
      protected void starting(Description description) {
        try {
          new FileWriter(Constants.TESTS_LOG).close();
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    };
  }

  /**
   * Subclasses should define their rules in this method so that they can be deterministically
   * ordered and properly nested relative to the global logging and timeout rules.
   *
   * @return the list of rules used by the subclass
   */
  protected List<TestRule> rules() {
    return Collections.EMPTY_LIST;
  }
}

@@ -560,16 +560,50 @@
 nstants;
+%0Aimport alluxio.util.io.PathUtils;
 %0A%0Aimport
@@ -655,16 +655,163 @@
 rnings;%0A
+import org.apache.log4j.Appender;%0Aimport org.apache.log4j.FileAppender;%0Aimport org.apache.log4j.LogManager;%0Aimport org.apache.log4j.PatternLayout;%0A
 import o
@@ -1170,49 +1170,8 @@
 hs;%0A
-import java.nio.file.StandardCopyOption;%0A
 impo
@@ -2148,42 +2148,83 @@
     
-// When tests fail, save the logs.
+private String mLogPath;%0A      private Appender mAppender;%0A%0A      @Override
 %0A   
@@ -2245,28 +2245,17 @@
 oid 
-failed(Throwable t, 
+starting(
 Desc
@@ -2305,419 +2305,505 @@
     
-Files.copy(Paths.get(Constants.TESTS_LOG),%0A              Paths.get(Constants.TEST_LOG_DIR
+mLogPath = logPath(description);%0A          // In case the file already exists
 , 
-S
 tr
-ing.format(%22%25s-%25s.log%22,%0A                  description.getClassName(), description.getMethodName())),%0A              StandardCopyOption.REPLACE_EXISTING);%0A        %7D catch (IOException e) %7B%0A          if (t != null) %7B%0A            t.addSuppressed(e);%0A          %7D else %7B%0A            throw new RuntimeException(e);%0A 
+uncate it.%0A          new FileWriter(mLogPath).close();%0A          mAppender = new FileAppender(new PatternLayout(%22%25d%7BISO8601%7D %25-5p %25c%7B2%7D (%25F:%25M) - %25m%25n%22),%0A              mLogPath);%0A          LogManager.getRootLogger().addAppender(mAppender);%0A        %7D catch (IOException e) %7B%0A          throw new RuntimeException(e);%0A        %7D%0A      %7D%0A%0A      @Override%0A      protected void succeeded(Description description) %7B%0A
         
  %7D%0A 
@@ -2794,26 +2794,29 @@
 ) %7B%0A        
- %7D
+try %7B
 %0A        %7D%0A 
@@ -2816,91 +2816,182 @@
     
-%7D%0A        return;
+  Files.delete(Paths.get(mLogPath));%0A        %7D catch (Throwable t) %7B
 %0A      
-%7D%0A%0A
     
-  // Before each test starts, truncate the log file.
+LOG.error(%22Failed to delete success log file %7B%7D%22, mLogPath);%0A        %7D%0A      %7D%0A%0A      @Override
 %0A   
@@ -3004,32 +3004,32 @@
 tected void 
-starting
+finished
 (Description
@@ -3056,153 +3056,326 @@
     
-try %7B%0A          new FileWriter(Constants.TESTS_LOG).close();%0A        %7D catch (IOException e) %7B%0A          throw new RuntimeException(e);%0A        %7D
+LogManager.getRootLogger().removeAppender(mAppender);%0A      %7D%0A%0A      private String logPath(Description description) %7B%0A        String basename = String.format(%22tests-%25s-%25s.log%22, description.getClassName(),%0A            description.getMethodName());%0A        return PathUtils.concatPath(Constants.TEST_LOG_DIR, basename);
 %0A   

Use a separate log file for every integration test (#7334)