// Copyright 2015 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.analysis.actions;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionInputHelper;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.ArtifactOwner;
import com.google.devtools.build.lib.actions.Executor;
import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
import com.google.devtools.build.lib.actions.Root;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.exec.util.TestExecutorBuilder;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Tests for ParamFileWriteAction. */
@RunWith(JUnit4.class)
public class ParamFileWriteActionTest extends BuildViewTestCase {
  private Root rootDir;
  private Artifact outputArtifact;
  private Artifact treeArtifact;

  @Before
  public void createArtifacts() throws Exception  {
    rootDir = Root.asDerivedRoot(scratch.dir("/exec/root"));
    outputArtifact = getBinArtifactWithNoOwner("destination.txt");
    FileSystemUtils.createDirectoryAndParents(outputArtifact.getPath().getParentDirectory());
    treeArtifact = createTreeArtifact("artifact/myTreeFileArtifact");
  }


  @Test
  public void testOutputs() {
    Action action = createParameterFileWriteAction(
        ImmutableList.<Artifact>of(), createNormalCommandLine());
    assertThat(Artifact.toRootRelativePaths(action.getOutputs())).containsExactly(
        "destination.txt");
  }

  @Test
  public void testInputs() {
    Action action = createParameterFileWriteAction(
        ImmutableList.of(treeArtifact),
        createTreeArtifactExpansionCommandLine());
    assertThat(Artifact.toExecPaths(action.getInputs())).containsExactly(
        "artifact/myTreeFileArtifact");
  }

  @Test
  public void testWriteCommandLineWithoutTreeArtifactExpansion() throws Exception {
    Action action = createParameterFileWriteAction(
        ImmutableList.<Artifact>of(), createNormalCommandLine());
    ActionExecutionContext context = actionExecutionContext();
    action.execute(context);
    String content = new String(FileSystemUtils.readContentAsLatin1(outputArtifact.getPath()));
    assertEquals("--flag1\n--flag2\n--flag3\nvalue1\nvalue2", content.trim());
  }

  @Test
  public void testWriteCommandLineWithTreeArtifactExpansion() throws Exception {
    Action action = createParameterFileWriteAction(
        ImmutableList.of(treeArtifact),
        createTreeArtifactExpansionCommandLine());
    ActionExecutionContext context = actionExecutionContext();
    action.execute(context);
    String content = new String(FileSystemUtils.readContentAsLatin1(outputArtifact.getPath()));
    assertEquals(
        "--flag1\n"
        + "artifact/myTreeFileArtifact/artifacts/treeFileArtifact1:"
        + "artifact/myTreeFileArtifact/artifacts/treeFileArtifact2",
        content.trim());
  }

  private Artifact createTreeArtifact(String rootRelativePath) {
    PathFragment relpath = new PathFragment(rootRelativePath);
    return new SpecialArtifact(
        rootDir.getPath().getRelative(relpath),
        rootDir,
        rootDir.getExecPath().getRelative(relpath),
        ArtifactOwner.NULL_OWNER,
        SpecialArtifactType.TREE);
  }

  private TreeFileArtifact createTreeFileArtifact(
      Artifact inputTreeArtifact, String parentRelativePath) {
    return ActionInputHelper.treeFileArtifact(
        inputTreeArtifact,
        new PathFragment(parentRelativePath));
  }

  private ParameterFileWriteAction createParameterFileWriteAction(
      Iterable<Artifact> inputTreeArtifacts, CommandLine commandLine) {
    return new ParameterFileWriteAction(
        ActionsTestUtil.NULL_ACTION_OWNER,
        inputTreeArtifacts,
        outputArtifact,
        commandLine,
        ParameterFileType.UNQUOTED,
        StandardCharsets.ISO_8859_1);
  }

  private CommandLine createNormalCommandLine() {
    return CustomCommandLine.builder()
        .add("--flag1")
        .add("--flag2")
        .add("--flag3", ImmutableList.of("value1", "value2"))
        .build();
  }

  private CommandLine createTreeArtifactExpansionCommandLine() {
    return CustomCommandLine.builder()
        .add("--flag1")
        .addJoinExpandedTreeArtifactExecPath(":", treeArtifact)
        .build();
  }

  private ActionExecutionContext actionExecutionContext() throws Exception {
    final Iterable<TreeFileArtifact> treeFileArtifacts = ImmutableList.of(
        createTreeFileArtifact(treeArtifact, "artifacts/treeFileArtifact1"),
        createTreeFileArtifact(treeArtifact, "artifacts/treeFileArtifact2"));

    ArtifactExpander artifactExpander = new ArtifactExpander() {
      @Override
      public void expand(Artifact artifact, Collection<? super Artifact> output) {
        for (TreeFileArtifact treeFileArtifact : treeFileArtifacts) {
          if (treeFileArtifact.getParent().equals(artifact)) {
            output.add(treeFileArtifact);
          }
        }
      }
    };

    Executor executor = new TestExecutorBuilder(directories, binTools).build();
    return new ActionExecutionContext(executor, null, null, new FileOutErr(),
        ImmutableMap.<String, String>of(), artifactExpander);
  }
}
