2016-10-11 5 views
1

に失敗する問題JUnitテストローカルに合格したがトラビスCI

私と私のグループのメンバーは、私たちのゲームのためにロガーを実装するために、私たち教師が指示されています。このロガーはJUnitで十分にテストされていなければなりません。今私たちがしばらく対処している問題の1つは、これらのテストがすべてローカルで行われていますが、今は毎回失敗していることです。

我々の分析

我々は、アサーションが実行される前にこれらのテストは、実際にログファイルを作成し、削除するための十分な時間がないと思われます。しかし、トラビスでテストが失敗する原因となっているのかどうかはわかりません。

私たちのコード

Logger.java

package logging; 

import java.io.BufferedReader; 
import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileReader; 
import java.io.FileWriter; 
import java.io.IOException; 
import java.nio.file.Files; 
import java.nio.file.StandardCopyOption; 
import java.sql.Timestamp; 
import java.util.Calendar; 

public final class Logger extends Thread { 
    private static volatile boolean debug = false; 
    private static volatile StringBuilder queue = new StringBuilder(); 
    private static volatile File file = new File("log.log"); 
    private static volatile int logLength = 10000; 
    private static Logger logger; 

    /** 
    * supported logging types. 
    * 
    */ 
    public enum LogType { 
     INFO, WARNING, ERROR 
    } 

    private Logger() { 
    } // unreachable because static 

    /** 
    * The logger runs in his own thread to prevent concurrent writing 
    * exceptions on the log file if multiple threads are logging. 
    */ 
    public void run() { 
     while (debug) { 
      try { 
       sleep(10000); 
       if (queue.length() > 0) { 
        try { 
         writeToFile(); 
        } catch (IOException exception) { 
         exception.printStackTrace(); 
        } 
       } 
      } catch (InterruptedException exception) { 
       exception.printStackTrace(); 
      } 
     } 
    } 

    private void writeToFile() throws IOException { 
     if (!file.exists()) { 
      file.createNewFile(); 
     } 
     FileWriter writer = new FileWriter(file, true); 
     writer.write(queue.toString()); 
     writer.close(); 
     capFileSize(); 
    } 

    private void capFileSize() throws IOException { 
     int fileLength = countLines(); 
     if (fileLength > logLength) { 
      String line; 
      File tempFile = File.createTempFile("TETRIS_LOG_", ".log"); 
      BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile)); 
      try { 
       BufferedReader reader = new BufferedReader(new FileReader(file)); 
       try { 
        skipLines(fileLength - logLength, reader); 
        while ((line = reader.readLine()) != null) { 
         writer.write(line); 
         writer.newLine(); 
        } 
       } finally { 
        reader.close(); 
       } 
      } finally { 
       writer.close(); 
      } 
      Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); 
     } 
    } 

    private void skipLines(int lines, BufferedReader file) throws IOException { 
     for (int i = 0; i < lines; i++) { 
      file.readLine(); 
     } 
    } 

    private int countLines() throws IOException { 
     char[] buffer = new char[1024]; 
     int count = 0; 
     int readChars = 0; 
     boolean empty = true; 
     BufferedReader reader = new BufferedReader(new FileReader(file)); 
     try { 
      while ((readChars = reader.read(buffer)) != -1) { 
       empty = false; 
       for (int i = 0; i < readChars; ++i) { 
        if (buffer[i] == '\n') { 
         ++count; 
        } 
       } 
      } 
      return (count == 0 && !empty) ? 1 : count; 
     } finally { 
      reader.close(); 
     } 
    } 

    /** 
    * Log lets you log a line in the log file, conditional on the debug mode 
    * being on. 
    * 
    * @param sender 
    *   the object invoking the log statement 
    * @param logtype 
    *   the log type, for homogeneity constrained in the LogType enum 
    * @param message 
    *   the message that is logged 
    */ 
    public static void log(Object sender, LogType logtype, String message) { 
     if (debug) { 
      String msg = String.format("[%s] message @[%s] from object %s: %s\r\n", 
       logtype.toString(), 
       new Timestamp(Calendar.getInstance().getTimeInMillis()).toString(), 
       sender.toString(), message); 
      queue.append(msg); 
     } 
    } 

    public static void info(Object sender, String message) { 
     Logger.log(sender, LogType.INFO, message); 
    } 

    public static void error(Object sender, String message) { 
     Logger.log(sender, LogType.ERROR, message); 
    } 

    public static void warning(Object sender, String message) { 
     Logger.log(sender, LogType.WARNING, message); 
    } 

    /** 
    * clearLog truncates the log, in case you accidentally logged a nude 
    * picture or something. 
    */ 
    public static void clearLog() { 
     try { 
      Files.deleteIfExists(file.toPath()); 
     } catch (IOException exception) { 
      exception.printStackTrace(); 
     } 
    } 

    /* getters/setters */ 
    public static int getLogLength() { 
     return logLength; 
    } 

    public static void setLogLength(int length) { 
     logLength = length; 
    } 

    public static void setLogDir(String path) { 
     file = new File(path); 
    } 

    public static String getLogDir() { 
     return file.toString(); 
    } 

    /** 
    * switch debug on. 
    */ 
    public static synchronized void setDebugOn() { 
     if (!debug) { 
      debug = true; 
      logger = new Logger(); 
      logger.start(); 
     } 
    } 

    /** 
    * switch debug off. 
    */ 
    public static void setDebugOff() { 
     if (debug) { 
      debug = false; 
      try { 
       logger.join(); 
      } catch (InterruptedException exception) { 
       exception.printStackTrace(); 
      } 
     } 
     logger = null; 
    } 
} 

LoggerTest.java

package logging; 

import java.io.BufferedReader; 
import java.io.File; 
import java.io.FileReader; 
import java.io.IOException; 

import org.junit.Test; 

import static org.junit.Assert.assertEquals; 
import static org.junit.Assert.assertFalse; 
import static org.junit.Assert.assertTrue; 

public class LoggerTest { 
    @Test 
    public void test_logcreate() { 
     String testloc = "test.log"; 
     Logger.setLogDir(testloc); 
     Logger.clearLog(); 
     Logger.setDebugOn(); 
     Logger.log(this, Logger.LogType.ERROR, "test 1"); 
     Logger.setDebugOff(); 
     assertTrue(new File(testloc).exists()); 
    } 

    @Test 
    public void test_logdelete() { 
     String testloc = "test.log"; 
     Logger.setLogDir(testloc); 
     Logger.setDebugOn(); 
     Logger.log(this, Logger.LogType.ERROR, "test 1"); 
     assertTrue(new File(testloc).exists()); 
     Logger.setDebugOff(); 
     Logger.clearLog(); 
     assertFalse(new File(testloc).exists()); 
    } 

    @Test 
    public void test_debugMode() { 
     String testloc = "test.log"; 
     Logger.setLogDir(testloc); 
     Logger.setDebugOff(); 
     Logger.clearLog(); 
     Logger.log(this, Logger.LogType.ERROR, "test 1"); 

     assertFalse(new File(testloc).exists()); 
    } 

    @Test 
    public void test_capLog() throws IOException, InterruptedException { 
     String testloc = "test.log"; 
     Logger.setLogDir(testloc); 
     Logger.setLogLength(10); 
     Logger.clearLog(); 
     Logger.setDebugOn(); 
     for (int i = 0; i < 100; i++) { 
      Logger.log(this, Logger.LogType.ERROR, "test 1"); 
     } 
     Logger.setDebugOff(); 
     Thread.sleep(1000); 
     assertTrue(new File(testloc).exists()); 

     int count = 0; 
     File file = new File(testloc); 
     FileReader fileReader = new FileReader(file); 
     BufferedReader reader = new BufferedReader(fileReader); 
     while (reader.readLine() != null) { 
      ++count; 
     } 
     reader.close(); 
     assertEquals(10, count); 

    } 
} 

トラヴィス・ジョブ・ログ

[...]

:test 
Download https://jcenter.bintray.com/org/jacoco/org.jacoco.agent/0.7.7.201606060606/org.jacoco.agent-0.7.7.201606060606.pom 
Download https://jcenter.bintray.com/org/jacoco/org.jacoco.build/0.7.7.201606060606/org.jacoco.build-0.7.7.201606060606.pom 
Download https://jcenter.bintray.com/org/jacoco/org.jacoco.agent/0.7.7.201606060606/org.jacoco.agent-0.7.7.201606060606.jar 
tetris.LoggerTest > test_logcreate FAILED 
    java.lang.AssertionError at LoggerTest.java:26 
tetris.LoggerTest > test_logdelete FAILED 
    java.lang.AssertionError at LoggerTest.java:35 
47 tests completed, 2 failed 
:test FAILED 
FAILURE: Build failed with an exception. 
* What went wrong: 
Execution failed for task ':test'. 

[...]

EDIT#1

私はAwaitilityを使用しているが、トラビスは、まだファイルの作成/削除で苦労しています。 1分のタイムアウトでも。あなたの最初のテストケースで

LoggerTest.java

package tetris; 

import java.io.BufferedReader; 
import java.io.File; 
import java.io.FileReader; 
import java.util.concurrent.Callable; 

import org.junit.Test; 

import static com.jayway.awaitility.Awaitility.with; 
import static com.jayway.awaitility.Duration.*; 
import static org.hamcrest.Matchers.equalTo; 
import static org.junit.Assert.assertEquals; 
import static org.junit.Assert.assertFalse; 
import static org.junit.Assert.assertTrue; 

import logging.Logger; 

public class LoggerTest { 

    @Test 
    public void test_logcreate() throws Exception { 
     String testloc = "test.log"; 
     Logger.setLogDir(testloc); 
     Logger.clearLog(); 
     Logger.setDebugOn(); 
     Logger.log(this, Logger.LogType.ERROR, "test 1"); 
     Logger.setDebugOff(); 

     asyncWaitForFileCreation(testloc); 
     assertTrue(new File(testloc).exists()); 
    } 

    @Test 
    public void test_logdelete() throws Exception { 
     String testloc = "test.log"; 
     Logger.setLogDir(testloc); 
     Logger.setDebugOn(); 
     Logger.log(this, Logger.LogType.ERROR, "test 1"); 

     asyncWaitForFileCreation(testloc); 
     assertTrue(new File(testloc).exists()); 

     Logger.setDebugOff(); 
     Logger.clearLog(); 

     asyncWaitForFileRemoval(testloc); 
     assertFalse(new File(testloc).exists()); 
    } 

    @Test 
    public void test_debugMode() throws Exception { 
     String testloc = "test.log"; 
     Logger.setLogDir(testloc); 
     Logger.setDebugOff(); 
     Logger.clearLog(); 
     Logger.log(this, Logger.LogType.ERROR, "test 1"); 

     asyncWaitForFileRemoval(testloc); 
     assertFalse(new File(testloc).exists()); 
    } 

    @Test 
    public void test_capLog() throws Exception { 
     String testloc = "test.log"; 
     Logger.setLogDir(testloc); 
     Logger.setLogLength(10); 
     Logger.clearLog(); 
     Logger.setDebugOn(); 
     for (int i = 0; i < 100; i++) { 
      Logger.log(this, Logger.LogType.ERROR, "test 1"); 
      Logger.info(this, "test 1"); 
      Logger.warning(this, "test 1"); 
      Logger.error(this, "test 1"); 
     } 
     Logger.setDebugOff(); 

     File testlocFile = new File(testloc); 

     asyncWaitForFileCreation(testloc); 
     assertTrue(testlocFile.exists()); 

     int count = 0; 
     File file = new File(testloc); 
     FileReader fileReader = new FileReader(file); 
     BufferedReader reader = new BufferedReader(fileReader); 
     while (reader.readLine() != null) { 
      ++count; 
     } 
     reader.close(); 

     assertEquals(10, count); 
    } 
    @Test 
    public void test_getters() throws ClassCastException { 
     assertTrue(Logger.getLogDir() instanceof String); 
     assertTrue(Logger.getLogLength() == Logger.getLogLength()); 
    } 

    private void asyncWaitForFileCreation(String testloc) throws Exception { 
     with().pollDelay(ONE_HUNDRED_MILLISECONDS) 
       .and().with().pollInterval(TWO_HUNDRED_MILLISECONDS) 
       .and().with().timeout(ONE_MINUTE) 
       .await("file creation") 
       .until(fileIsCreatedOnDisk(testloc), equalTo(true)); 
    } 

    private void asyncWaitForFileRemoval(String testloc) throws Exception { 
     with().pollDelay(ONE_HUNDRED_MILLISECONDS) 
       .and().with().pollInterval(TWO_HUNDRED_MILLISECONDS) 
       .and().with().timeout(ONE_MINUTE) 
       .await("file removal") 
       .until(fileIsRemovedFromDisk(testloc), equalTo(true)); 
    } 

    private Callable<Boolean> fileIsCreatedOnDisk(final String filename) { 
     return() -> { 
      File file = new File(filename); 
      return file.exists(); 
     }; 
    } 

    private Callable<Boolean> fileIsRemovedFromDisk(final String filename) { 
     return() -> { 
      File file = new File(filename); 
      return !file.exists(); 
     }; 
    } 
} 

トラヴィス・ジョブ・ログ

:test 
Download https://jcenter.bintray.com/org/jacoco/org.jacoco.agent/0.7.7.201606060606/org.jacoco.agent-0.7.7.201606060606.pom 
Download https://jcenter.bintray.com/org/jacoco/org.jacoco.build/0.7.7.201606060606/org.jacoco.build-0.7.7.201606060606.pom 
Download https://jcenter.bintray.com/org/jacoco/org.jacoco.agent/0.7.7.201606060606/org.jacoco.agent-0.7.7.201606060606.jar 
tetris.LoggerTest > test_logcreate FAILED 
    java.util.concurrent.TimeoutException at LoggerTest.java:36 
tetris.LoggerTest > test_capLog FAILED 
    java.util.concurrent.TimeoutException at LoggerTest.java:94 
47 tests completed, 2 failed 
:test FAILED 
FAILURE: Build failed with an exception. 
* What went wrong: 
Execution failed for task ':test'. 

答えて

0

、あなたはロガースレッドにファイルを作成するための十分な時間を与えます。これはいくつかのOSで動作するかもしれませんが、Travis CIはsloooowです。推奨事項:一定の間隔(たとえば100ms)でしばらく(少なくとも5秒)条件(この場合はファイルが存在する)をポーリングするメソッドを作成します。

private static boolean pollForCondition(Callable<Boolean> condition) { 
    while (/* ... */) { 
    if (condition.call().booleanValue()) { 
     return true; 
    } 
    // ... 
    } 
    return false; 
} 

この方法は、すべてのテストケース(assertTrue()から)で使用する必要があります。

また、テストケースの実行順序が決定されていないことを考慮してください(Java 6または7、AFAIKで始まる)。次のテストケースが始まる前に、作成したファイルをどこかに削除しますか?

+0

私はあなたの提案を、運がなくても適用しようとしました。私の編集をもう一度見てもらえますか? – Pascal

+0

最後の点を確認しましたか? Travisの実行命令は、ローカル実行命令とは異なる場合があります。それらの間を掃除することなく、ローカルでテストを1つずつ実行してみましょう。逆順。 –

+0

test_logcreate、test_logdelete、test_capLog、test_getters、test_debugModeの順番はローカルで動作するようです。 ... test_capLog、test_debugMode、test_getters、test_logcreate、test_logdeleteの順番は失敗します。 – Pascal

関連する問題