/* * Copyright 2016-present Facebook, Inc. * * 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.facebook.buck.rage; import static com.facebook.buck.rage.AbstractRageConfig.RageProtocolVersion; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import com.facebook.buck.cli.BuckConfig; import com.facebook.buck.cli.FakeBuckConfig; import com.facebook.buck.event.BuckEventBusFactory; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.testutil.TestBuildEnvironmentDescription; import com.facebook.buck.testutil.TestConsole; import com.facebook.buck.testutil.integration.HttpdForTests; import com.facebook.buck.testutil.integration.ProjectWorkspace; import com.facebook.buck.testutil.integration.TemporaryPaths; import com.facebook.buck.testutil.integration.TestDataHelper; import com.facebook.buck.testutil.integration.ZipInspector; import com.facebook.buck.timing.Clock; import com.facebook.buck.timing.DefaultClock; import com.facebook.buck.util.Console; import com.facebook.buck.util.DefaultProcessExecutor; import com.facebook.buck.util.ObjectMappers; import com.facebook.buck.util.versioncontrol.NoOpCmdLineInterface; import com.facebook.buck.util.versioncontrol.VersionControlStatsGenerator; import com.google.common.base.Preconditions; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; import java.io.DataOutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class RageCommandIntegrationTest { private static final String UPLOAD_PATH = "/rage"; @Rule public TemporaryPaths temporaryFolder = new TemporaryPaths(); @Rule public ExpectedException expectedException = ExpectedException.none(); private static final String BUILD_COMMAND_DIR_PATH = "buck-out/log/" + "2016-06-21_16h16m24s_buildcommand_ac8bd626-6137-4747-84dd-5d4f215c876c/"; private static final String AUTODEPS_COMMAND_DIR_PATH = "buck-out/log/" + "2016-06-21_16h18m51s_autodepscommand_d09893d5-b11e-4e3f-a5bf-70c60a06896e/"; @Test public void testRageNonInteractiveReport() throws Exception { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "report", temporaryFolder); workspace.setUp(); workspace.runBuckCommand("rage", "--non-interactive").assertSuccess(); } @Test public void testUpload() throws Exception { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "report", temporaryFolder); workspace.setUp(); final AtomicReference<String> requestMethod = new AtomicReference<>(); final AtomicReference<String> requestPath = new AtomicReference<>(); final AtomicReference<byte[]> requestBody = new AtomicReference<>(); final String successMessage = "Upload successful"; try (HttpdForTests httpd = new HttpdForTests()) { httpd.addHandler( new AbstractHandler() { @Override public void handle( String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException { httpServletResponse.setStatus(200); request.setHandled(true); if (request.getUri().getPath().equals("/status.php")) { return; } requestPath.set(request.getUri().getPath()); requestMethod.set(request.getMethod()); requestBody.set(ByteStreams.toByteArray(httpServletRequest.getInputStream())); try (DataOutputStream out = new DataOutputStream(httpServletResponse.getOutputStream())) { out.writeBytes(successMessage); } } }); httpd.start(); RageConfig rageConfig = createRageConfig(httpd.getRootUri().getPort(), "", RageProtocolVersion.SIMPLE); ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot()); Clock clock = new DefaultClock(); DefectReporter reporter = new DefaultDefectReporter( filesystem, rageConfig, BuckEventBusFactory.newInstance(clock), clock); AutomatedReport automatedReport = new AutomatedReport( reporter, filesystem, new TestConsole(), TestBuildEnvironmentDescription.INSTANCE, new VersionControlStatsGenerator(new NoOpCmdLineInterface(), Optional.empty()), false, rageConfig, Optional::empty); DefectSubmitResult defectSubmitResult = automatedReport.collectAndSubmitResult().get(); assertThat( defectSubmitResult.getReportSubmitMessage(), Matchers.equalTo(Optional.of(successMessage))); assertThat(requestPath.get(), Matchers.equalTo(UPLOAD_PATH)); assertThat(requestMethod.get(), Matchers.equalTo("POST")); filesystem.mkdirs(filesystem.getBuckPaths().getBuckOut()); Path report = filesystem.createTempFile(filesystem.getBuckPaths().getBuckOut(), "report", "zip"); filesystem.writeBytesToPath(requestBody.get(), report); ZipInspector zipInspector = new ZipInspector(filesystem.resolve(report)); zipInspector.assertFileExists("report.json"); zipInspector.assertFileExists("buckconfig.local"); zipInspector.assertFileExists("bucklogging.local.properties"); zipInspector.assertFileExists(BUILD_COMMAND_DIR_PATH + "buck.log"); zipInspector.assertFileExists(AUTODEPS_COMMAND_DIR_PATH + "buck.log"); zipInspector.assertFileExists(BUILD_COMMAND_DIR_PATH + "buck-machine-log"); zipInspector.assertFileExists(AUTODEPS_COMMAND_DIR_PATH + "buck-machine-log"); } } @Test public void testExtraInfo() throws Exception { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "report", temporaryFolder); workspace.setUp(); RageConfig rageConfig = createRageConfig(0, "python, extra.py", RageProtocolVersion.SIMPLE); ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot()); Console console = new TestConsole(); CapturingDefectReporter defectReporter = new CapturingDefectReporter(); AutomatedReport automatedReport = new AutomatedReport( defectReporter, filesystem, new TestConsole(), TestBuildEnvironmentDescription.INSTANCE, new VersionControlStatsGenerator(new NoOpCmdLineInterface(), Optional.empty()), false, rageConfig, new DefaultExtraInfoCollector( rageConfig, filesystem, new DefaultProcessExecutor(console))); automatedReport.collectAndSubmitResult(); DefectReport defectReport = defectReporter.getDefectReport(); assertThat(defectReport.getExtraInfo(), Matchers.equalTo(Optional.of("Extra\n"))); assertThat( FluentIterable.from(defectReport.getIncludedPaths()).transform(Object::toString), Matchers.hasItem(Matchers.endsWith("extra.txt"))); } @Test public void testUploadFailure() throws Exception { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "report", temporaryFolder); workspace.setUp(); try (HttpdForTests httpd = new HttpdForTests()) { httpd.addHandler( new AbstractHandler() { @Override public void handle( String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException { httpServletResponse.setStatus(500); request.setHandled(true); } }); httpd.start(); RageConfig rageConfig = createRageConfig(httpd.getRootUri().getPort(), "", RageProtocolVersion.SIMPLE); ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot()); Clock clock = new DefaultClock(); DefectReporter reporter = new DefaultDefectReporter( filesystem, rageConfig, BuckEventBusFactory.newInstance(clock), clock); AutomatedReport automatedReport = new AutomatedReport( reporter, filesystem, new TestConsole(), TestBuildEnvironmentDescription.INSTANCE, new VersionControlStatsGenerator(new NoOpCmdLineInterface(), Optional.empty()), false, rageConfig, Optional::empty); DefectSubmitResult submitReport = automatedReport.collectAndSubmitResult().get(); // If upload fails it should store the zip locally and inform the user. assertFalse(submitReport.getReportSubmitErrorMessage().get().isEmpty()); ZipInspector zipInspector = new ZipInspector(filesystem.resolve(submitReport.getReportSubmitLocation().get())); assertEquals(zipInspector.getZipFileEntries().size(), 7); } } @Test public void testJsonUpload() throws Exception { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "report", temporaryFolder); workspace.setUp(); try (HttpdForTests httpd = new HttpdForTests()) { httpd.addHandler( new AbstractHandler() { @Override public void handle( String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpResponse) throws IOException, ServletException { httpResponse.setStatus(200); request.setHandled(true); if (request.getUri().getPath().equals("/status.php")) { return; } RageJsonResponse json = RageJsonResponse.of( /* isRequestSuccessful */ true, /* errorMessage */ Optional.empty(), /* rageUrl */ Optional.of("http://remoteUrlToVisit"), /* message */ Optional.of("This is supposed to be JSON.")); try (DataOutputStream out = new DataOutputStream(httpResponse.getOutputStream())) { ObjectMappers.WRITER.writeValue(out, json); } } }); httpd.start(); RageConfig rageConfig = createRageConfig(httpd.getRootUri().getPort(), "", RageProtocolVersion.JSON); ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot()); Clock clock = new DefaultClock(); DefectReporter reporter = new DefaultDefectReporter( filesystem, rageConfig, BuckEventBusFactory.newInstance(clock), clock); AutomatedReport automatedReport = new AutomatedReport( reporter, filesystem, new TestConsole(), TestBuildEnvironmentDescription.INSTANCE, new VersionControlStatsGenerator(new NoOpCmdLineInterface(), Optional.empty()), false, rageConfig, Optional::empty); DefectSubmitResult submitReport = automatedReport.collectAndSubmitResult().get(); assertEquals("http://remoteUrlToVisit", submitReport.getReportSubmitLocation().get()); assertEquals("This is supposed to be JSON.", submitReport.getReportSubmitMessage().get()); } } private RageConfig createRageConfig(int port, String extraInfo, RageProtocolVersion version) { BuckConfig buckConfig = FakeBuckConfig.builder() .setSections( ImmutableMap.of( RageConfig.RAGE_SECTION, ImmutableMap.of( RageConfig.REPORT_UPLOAD_PATH_FIELD, UPLOAD_PATH, RageConfig.PROTOCOL_VERSION_FIELD, version.name(), "slb_server_pool", "http://localhost:" + port, RageConfig.EXTRA_INFO_COMMAND_FIELD, extraInfo))) .build(); return RageConfig.of(buckConfig); } private static class CapturingDefectReporter implements DefectReporter { private DefectReport defectReport = null; public DefectReport getDefectReport() { return Preconditions.checkNotNull(defectReport); } @Override public DefectSubmitResult submitReport(DefectReport defectReport) throws IOException { this.defectReport = defectReport; return DefectSubmitResult.builder() .setRequestProtocol(RageProtocolVersion.SIMPLE) .setReportSubmitLocation("") .build(); } } }