<commit_before>/*
 * Copyright 2012-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.testutil.integration;

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

import com.facebook.buck.cli.Main;
import com.facebook.buck.cli.TestCommand;
import com.facebook.buck.rules.DefaultKnownBuildRuleTypes;
import com.facebook.buck.testutil.TestConsole;
import com.facebook.buck.util.CapturingPrintStream;
import com.facebook.buck.util.MoreFiles;
import com.facebook.buck.util.MoreStrings;
import com.facebook.buck.util.ProcessExecutor;
import com.facebook.buck.util.environment.Platform;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import com.martiansoftware.nailgun.NGClientListener;
import com.martiansoftware.nailgun.NGContext;

import org.junit.rules.TemporaryFolder;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.Properties;

import javax.annotation.Nullable;

/**
 * {@link ProjectWorkspace} is a directory that contains a Buck project, complete with build files.
 * <p>
 * When {@link #setUp()} is invoked, the project files are cloned from a directory of testdata into
 * a tmp directory according to the following rule:
 * <ul>
 *   <li>Files with the {@code .expected} extension will not be copied.
 * </ul>
 * After {@link #setUp()} is invoked, the test should invoke Buck in that directory. As this is an
 * integration test, we expect that files will be written as a result of invoking Buck.
 * <p>
 * After Buck has been run, invoke {@link #verify()} to verify that Buck wrote the correct files.
 * For each file in the testdata directory with the {@code .expected} extension, {@link #verify()}
 * will check that a file with the same relative path (but without the {@code .expected} extension)
 * exists in the tmp directory. If not, {@link org.junit.Assert#fail()} will be invoked.
 */
public class ProjectWorkspace {

  private static final String EXPECTED_SUFFIX = ".expected";

  private static final String PATH_TO_BUILD_LOG = "buck-out/bin/build.log";

  private static final Function<Path, Path> BUILD_FILE_RENAME = new Function<Path, Path>() {
    @Override
    @Nullable
    public Path apply(Path path) {
      String fileName = path.getFileName().toString();
      if (fileName.endsWith(EXPECTED_SUFFIX)) {
        return null;
      } else {
        return path;
      }
    }
  };

  private boolean isSetUp = false;
  private final Path templatePath;
  private final File destDir;
  private final Path destPath;

  /**
   * @param templateDir The directory that contains the template version of the project.
   * @param temporaryFolder The directory where the clone of the template directory should be
   *     written. By requiring a {@link TemporaryFolder} rather than a {@link File}, we can ensure
   *     that JUnit will clean up the test correctly.
   */
  public ProjectWorkspace(File templateDir, DebuggableTemporaryFolder temporaryFolder) {
    Preconditions.checkNotNull(templateDir);
    Preconditions.checkNotNull(temporaryFolder);
    this.templatePath = templateDir.toPath();
    this.destDir = temporaryFolder.getRoot();
    this.destPath = destDir.toPath();
  }

  /**
   * This will copy the template directory, renaming files named {@code BUCK.test} to {@code BUCK}
   * in the process. Files whose names end in {@code .expected} will not be copied.
   */
  public void setUp() throws IOException {
    DefaultKnownBuildRuleTypes.resetInstance();

    MoreFiles.copyRecursively(templatePath, destPath, BUILD_FILE_RENAME);

    // Copy local.properties, if it exists
    Path localProperties = FileSystems.getDefault().getPath("local.properties");
    try {
      java.nio.file.Files.copy(localProperties, destPath.resolve(localProperties.getFileName()));
    } catch (SecurityException|IOException e) {
      // Probably not a fatal error, just log the details and continue
      System.err.println("Error on attempting to copy local.properties: " + e);
    }

    if (Platform.detect() == Platform.WINDOWS) {
      // Hack for symlinks on Windows.
      SimpleFileVisitor<Path> copyDirVisitor = new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
          // On Windows, symbolic links from git repository are checked out as normal files
          // containing a one-line path. In order to distinguish them, paths are read and pointed
          // files are trued to locate. Once the pointed file is found, it will be copied to target.
          // On NTFS length of path must be greater than 0 and less than 4096.
          if (attrs.size() > 0 && attrs.size() <= 4096) {
            File file = path.toFile();
            String linkTo = Files.toString(file, Charsets.UTF_8);
            File linkToFile = new File(templatePath.toFile(), linkTo);
            if (linkToFile.isFile()) {
              java.nio.file.Files.copy(<commit_msg>Handle errors with less noise on STDERR<commit_after>/*
 * Copyright 2012-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.testutil.integration;

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

import com.facebook.buck.cli.Main;
import com.facebook.buck.cli.TestCommand;
import com.facebook.buck.rules.DefaultKnownBuildRuleTypes;
import com.facebook.buck.testutil.TestConsole;
import com.facebook.buck.util.CapturingPrintStream;
import com.facebook.buck.util.MoreFiles;
import com.facebook.buck.util.MoreStrings;
import com.facebook.buck.util.ProcessExecutor;
import com.facebook.buck.util.environment.Platform;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import com.martiansoftware.nailgun.NGClientListener;
import com.martiansoftware.nailgun.NGContext;

import org.junit.rules.TemporaryFolder;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.Properties;

import javax.annotation.Nullable;

/**
 * {@link ProjectWorkspace} is a directory that contains a Buck project, complete with build files.
 * <p>
 * When {@link #setUp()} is invoked, the project files are cloned from a directory of testdata into
 * a tmp directory according to the following rule:
 * <ul>
 *   <li>Files with the {@code .expected} extension will not be copied.
 * </ul>
 * After {@link #setUp()} is invoked, the test should invoke Buck in that directory. As this is an
 * integration test, we expect that files will be written as a result of invoking Buck.
 * <p>
 * After Buck has been run, invoke {@link #verify()} to verify that Buck wrote the correct files.
 * For each file in the testdata directory with the {@code .expected} extension, {@link #verify()}
 * will check that a file with the same relative path (but without the {@code .expected} extension)
 * exists in the tmp directory. If not, {@link org.junit.Assert#fail()} will be invoked.
 */
public class ProjectWorkspace {

  private static final String EXPECTED_SUFFIX = ".expected";

  private static final String PATH_TO_BUILD_LOG = "buck-out/bin/build.log";

  private static final Function<Path, Path> BUILD_FILE_RENAME = new Function<Path, Path>() {
    @Override
    @Nullable
    public Path apply(Path path) {
      String fileName = path.getFileName().toString();
      if (fileName.endsWith(EXPECTED_SUFFIX)) {
        return null;
      } else {
        return path;
      }
    }
  };

  private boolean isSetUp = false;
  private final Path templatePath;
  private final File destDir;
  private final Path destPath;

  /**
   * @param templateDir The directory that contains the template version of the project.
   * @param temporaryFolder The directory where the clone of the template directory should be
   *     written. By requiring a {@link TemporaryFolder} rather than a {@link File}, we can ensure
   *     that JUnit will clean up the test correctly.
   */
  public ProjectWorkspace(File templateDir, DebuggableTemporaryFolder temporaryFolder) {
    Preconditions.checkNotNull(templateDir);
    Preconditions.checkNotNull(temporaryFolder);
    this.templatePath = templateDir.toPath();
    this.destDir = temporaryFolder.getRoot();
    this.destPath = destDir.toPath();
  }

  /**
   * This will copy the template directory, renaming files named {@code BUCK.test} to {@code BUCK}
   * in the process. Files whose names end in {@code .expected} will not be copied.
   */
  public void setUp() throws IOException {
    DefaultKnownBuildRuleTypes.resetInstance();

    MoreFiles.copyRecursively(templatePath, destPath, BUILD_FILE_RENAME);

    // If there's a local.properties in the host project but not in the destination, make a copy.
    Path localProperties = FileSystems.getDefault().getPath("local.properties");
    Path destLocalProperties = destPath.resolve(localProperties.getFileName());

    if (localProperties.toFile().exists() && !destLocalProperties.toFile().exists()) {
      java.nio.file.Files.copy(localProperties, destLocalProperties);
    }

    if (Platform.detect() == Platform.WINDOWS) {
      // Hack for symlinks on Windows.
      SimpleFileVisitor<Path> copyDirVisitor = new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
          // On Windows, symbolic links from git repository are checked out as normal files
          // containing a one-line path. In order to distinguish them, paths are read and pointed
          // files are trued to locate. Once the pointed file is found, it will be copied to target.
          // On NTFS length of path must be greater than 0 and less than 4096.
          if (attrs.size() > 0 && attrs.size() <= 4096) {
            File file = path.toFile();
            String linkTo = Files.toString(file, Charsets.UTF_8);
            File linkToFile = new File(templatePath.toFile(), linkTo);
            if (linkToFile.isFile()) {
              java.nio.file.Files.copy(
{'fraction_non_alphanumeric': 0.09409927679158449, 'fraction_numerical': 0.0031229454306377384, 'mean_word_length': 3.8251387787470263, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6816204', 'n_tokens_mistral': 3335, 'n_tokens_neox': 3279, 'n_words': 1164}