/*
 * Copyright 2017 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.idea.blaze.aspect;

import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.idea.blaze.aspect.JarFilter.JarFilterOptions;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Unit tests for {@link JarFilter} */
@RunWith(JUnit4.class)
public class JarFilterTest {

  @Rule public TemporaryFolder folder = new TemporaryFolder();

  @Test
  public void testFilterMethod() throws Exception {
    List<String> prefixes =
        ImmutableList.of("com/google/foo/Foo", "com/google/bar/Bar", "com/google/baz/Baz");
    assertThat(JarFilter.shouldKeepClass(prefixes, "com/google/foo/Foo.class")).isTrue();
    assertThat(JarFilter.shouldKeepClass(prefixes, "com/google/foo/Foo$Inner.class")).isTrue();
    assertThat(JarFilter.shouldKeepClass(prefixes, "com/google/bar/Bar.class")).isTrue();
    assertThat(JarFilter.shouldKeepClass(prefixes, "com/google/foo/Foo/NotFoo.class")).isFalse();
    assertThat(JarFilter.shouldKeepClass(prefixes, "wrong/com/google/foo/Foo.class")).isFalse();
  }

  @Test
  public void fullIntegrationTest() throws Exception {
    File fooJava = folder.newFile("Foo.java");
    Files.write("package com.google.foo; class Foo { class Inner {} }".getBytes(UTF_8), fooJava);

    File barJava = folder.newFile("Bar.java");
    Files.write("package com.google.foo.bar; class Bar {}".getBytes(UTF_8), barJava);

    File srcJar = folder.newFile("gen.srcjar");
    try (ZipOutputStream zo = new ZipOutputStream(new FileOutputStream(srcJar))) {
      zo.putNextEntry(new ZipEntry("com/google/foo/gen/Gen.java"));
      zo.write("package gen; class Gen {}".getBytes(UTF_8));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("com/google/foo/gen/Gen2.java"));
      zo.write("package gen; class Gen2 {}".getBytes(UTF_8));
      zo.closeEntry();
    }

    File src3Jar = folder.newFile("gen3.srcjar");
    try (ZipOutputStream zo = new ZipOutputStream(new FileOutputStream(src3Jar))) {
      zo.putNextEntry(new ZipEntry("com/google/foo/gen/Gen3.java"));
      zo.write("package gen; class Gen3 {}".getBytes(UTF_8));
      zo.closeEntry();
    }

    File filterJar = folder.newFile("foo.jar");
    try (ZipOutputStream zo = new ZipOutputStream(new FileOutputStream(filterJar))) {
      zo.putNextEntry(new ZipEntry("com/google/foo/Foo.class"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("com/google/foo/Foo$Inner.class"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("com/google/foo/bar/Bar.class"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("gen/Gen.class"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("gen/Gen2.class"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("gen/Gen3.class"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("com/google/foo/Foo2.class"));
      zo.closeEntry();
    }
    File filterSrcJar = folder.newFile("foo-src.jar");
    try (ZipOutputStream zo = new ZipOutputStream(new FileOutputStream(filterSrcJar))) {
      zo.putNextEntry(new ZipEntry("com/google/foo/Foo.java"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("com/google/foo/bar/Bar.java"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("gen/Gen.java"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("gen/Gen2.java"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("gen/Gen3.java"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("com/google/foo/Foo2.java"));
      zo.closeEntry();
      zo.putNextEntry(new ZipEntry("com/google/foo/bar/Bar2.java"));
      zo.closeEntry();
    }

    File filteredJar = folder.newFile("foo-filtered-gen.jar");
    File filteredSourceJar = folder.newFile("foo-filtered-gen-src.jar");

    String[] args =
        new String[] {
          "--keep_java_file",
          fooJava.getPath(),
          "--keep_java_file",
          barJava.getPath(),
          "--keep_source_jar",
          srcJar.getPath(),
          "--keep_source_jar",
          src3Jar.getPath(),
          "--filter_jar",
          filterJar.getPath(),
          "--filter_source_jar",
          filterSrcJar.getPath(),
          "--filtered_jar",
          filteredJar.getPath(),
          "--filtered_source_jar",
          filteredSourceJar.getPath()
        };
    JarFilterOptions options = JarFilter.parseArgs(args);
    JarFilter.main(options);

    List<String> filteredJarNames = Lists.newArrayList();
    try (ZipFile zipFile = new ZipFile(filteredJar)) {
      Enumeration<? extends ZipEntry> entries = zipFile.entries();
      while (entries.hasMoreElements()) {
        ZipEntry zipEntry = entries.nextElement();
        filteredJarNames.add(zipEntry.getName());
      }
    }

    List<String> filteredSourceJarNames = Lists.newArrayList();
    try (ZipFile zipFile = new ZipFile(filteredSourceJar)) {
      Enumeration<? extends ZipEntry> entries = zipFile.entries();
      while (entries.hasMoreElements()) {
        ZipEntry zipEntry = entries.nextElement();
        filteredSourceJarNames.add(zipEntry.getName());
      }
    }

    assertThat(filteredJarNames)
        .containsExactly(
            "com/google/foo/Foo.class",
            "com/google/foo/Foo$Inner.class",
            "com/google/foo/bar/Bar.class",
            "gen/Gen.class",
            "gen/Gen2.class",
            "gen/Gen3.class");

    assertThat(filteredSourceJarNames)
        .containsExactly(
            "com/google/foo/Foo.java",
            "com/google/foo/bar/Bar.java",
            "gen/Gen.java",
            "gen/Gen2.java",
            "gen/Gen3.java");
  }
}
