/hudson-core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java

http://github.com/hudson/hudson · Java · 119 lines · 49 code · 12 blank · 58 comment · 5 complexity · 9a3b29abb7cc4c34a350de49861a4741 MD5 · raw file

  1. /*
  2. * The MIT License
  3. *
  4. * Copyright (c) 2004-2009, Sun Microsystems, Inc.
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. package hudson.tasks.test;
  25. import hudson.AbortException;
  26. import hudson.FilePath;
  27. import hudson.FilePath.FileCallable;
  28. import hudson.Launcher;
  29. import hudson.Util;
  30. import hudson.model.AbstractBuild;
  31. import hudson.model.TaskListener;
  32. import hudson.remoting.VirtualChannel;
  33. import java.io.File;
  34. import java.io.IOException;
  35. import java.io.Serializable;
  36. import java.util.ArrayList;
  37. import java.util.List;
  38. /**
  39. * Default partial implementation of {@link TestResultParser} that handles GLOB dereferencing
  40. * and other checks for user errors, such as misconfigured GLOBs, up-to-date checks on test reports.
  41. *
  42. * <p>
  43. * The instance of the parser will be serialized to the node that performed the build and the parsing will be done
  44. * remotely on that slave.
  45. *
  46. * @since 1.343
  47. * @author Kohsuke Kawaguchi
  48. */
  49. public abstract class DefaultTestResultParserImpl extends TestResultParser implements Serializable {
  50. /**
  51. * This method is executed on the slave that has the report files to parse test reports and builds {@link TestResult}.
  52. *
  53. * @param reportFiles
  54. * List of files to be parsed. Never be empty nor null.
  55. * @param launcher
  56. * Can be used to fork processes on the machine where the build is running. Never null.
  57. * @param listener
  58. * Use this to report progress and other problems. Never null.
  59. *
  60. * @throws InterruptedException
  61. * If the user cancels the build, it will be received as a thread interruption. Do not catch
  62. * it, and instead just forward that through the call stack.
  63. * @throws IOException
  64. * If you don't care about handling exceptions gracefully, you can just throw IOException
  65. * and let the default exception handling in Hudson takes care of it.
  66. * @throws AbortException
  67. * If you encounter an error that you handled gracefully, throw this exception and Hudson
  68. * will not show a stack trace.
  69. */
  70. protected abstract TestResult parse(List<File> reportFiles, Launcher launcher, TaskListener listener) throws InterruptedException, IOException;
  71. @Override
  72. public TestResult parse(final String testResultLocations, final AbstractBuild build, final Launcher launcher, final TaskListener listener) throws InterruptedException, IOException {
  73. return build.getWorkspace().act(new FileCallable<TestResult>() {
  74. final boolean ignoreTimestampCheck = IGNORE_TIMESTAMP_CHECK; // so that the property can be set on the master
  75. final long buildTime = build.getTimestamp().getTimeInMillis();
  76. final long nowMaster = System.currentTimeMillis();
  77. public TestResult invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException {
  78. final long nowSlave = System.currentTimeMillis();
  79. // files older than this timestamp is considered stale
  80. long localBuildTime = buildTime + (nowSlave - nowMaster);
  81. FilePath[] paths = new FilePath(dir).list(testResultLocations);
  82. if (paths.length==0)
  83. throw new AbortException("No test reports that matches "+testResultLocations+" found. Configuration error?");
  84. // since dir is local, paths all point to the local files
  85. List<File> files = new ArrayList<File>(paths.length);
  86. for (FilePath path : paths) {
  87. File report = new File(path.getRemote());
  88. if (ignoreTimestampCheck || localBuildTime - 3000 /*error margin*/ < report.lastModified()) {
  89. // this file is created during this build
  90. files.add(report);
  91. }
  92. }
  93. if (files.isEmpty()) {
  94. // none of the files were new
  95. throw new AbortException(
  96. String.format(
  97. "Test reports were found but none of them are new. Did tests run? \n"+
  98. "For example, %s is %s old\n", paths[0].getRemote(),
  99. Util.getTimeSpanString(localBuildTime-paths[0].lastModified())));
  100. }
  101. return parse(files,launcher,listener);
  102. }
  103. });
  104. }
  105. private static final long serialVersionUID = 1L;
  106. public static final boolean IGNORE_TIMESTAMP_CHECK = Boolean.getBoolean(TestResultParser.class.getName()+".ignoreTimestampCheck");
  107. }