diff options
author | 2017-01-25 10:14:52 +0000 | |
---|---|---|
committer | 2017-01-25 13:05:26 +0000 | |
commit | 81ea54e5545803da0e8c0f7402305d5619765f0e (patch) | |
tree | 6df6c01d4141495d6a9a60e3739800b7c0d04cec /src | |
parent | c4e92b1ba06fba48121e4db8050a69b6d998e2e9 (diff) |
Benchmark part 1: generate java code for build
--
PiperOrigin-RevId: 145524911
MOS_MIGRATED_REVID=145524911
Diffstat (limited to 'src')
11 files changed, 1141 insertions, 0 deletions
@@ -321,6 +321,7 @@ filegroup( "//src/test/java/com/google/devtools/common/options:srcs", "//src/test/shell:srcs", "//src/tools/android/java/com/google/devtools/build/android:srcs", + "//src/tools/benchmark:srcs", "//src/tools/generate_workspace:srcs", "//src/tools/xcode/actoolwrapper:srcs", "//src/tools/xcode/environment:srcs", diff --git a/src/tools/benchmark/BUILD b/src/tools/benchmark/BUILD new file mode 100644 index 0000000000..c2678f81ce --- /dev/null +++ b/src/tools/benchmark/BUILD @@ -0,0 +1,8 @@ +filegroup( + name = "srcs", + srcs = glob(["**"]) + [ + "//src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator:srcs", + "//src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator:srcs", + ], + visibility = ["//src:__pkg__"], +) diff --git a/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/BUILD b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/BUILD new file mode 100644 index 0000000000..4285252bd8 --- /dev/null +++ b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/BUILD @@ -0,0 +1,28 @@ +package(default_visibility = ["//src/tools/benchmark:__subpackages__"]) + +java_binary( + name = "codegenerator", + srcs = glob(["*.java"]), + main_class = "com.google.devtools.build.benchmark.codegenerator.Main", + deps = [ + "//src/main/java/com/google/devtools/common/options", + "//third_party:guava", + "//third_party/java/javapoet", + ], +) + +java_library( + name = "codegenerator_lib", + srcs = glob(["*.java"]), + deps = [ + "//src/main/java/com/google/devtools/common/options", + "//third_party:guava", + "//third_party/java/javapoet", + ], +) + +filegroup( + name = "srcs", + srcs = glob(["**"]), + visibility = ["//src:__pkg__"], +) diff --git a/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/GeneratorOptions.java b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/GeneratorOptions.java new file mode 100644 index 0000000000..dd3d79346b --- /dev/null +++ b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/GeneratorOptions.java @@ -0,0 +1,71 @@ +// 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.devtools.build.benchmark.codegenerator; + +import com.google.devtools.common.options.Option; +import com.google.devtools.common.options.OptionsBase; + +/** Class that contains arguments for the java files generator. */ +public class GeneratorOptions extends OptionsBase { + + @Option( + name = "modify", + defaultValue = "false", + category = "generator", + help = "if we modify the existing code (or generate new code)." + ) + public boolean modificationMode; + + @Option( + name = "output_dir", + defaultValue = "", + category = "generator", + valueHelp = "path", + help = "directory where we put generated code or modify the existing code." + ) + public String outputDir; + + @Option( + name = "a_few_files", + defaultValue = "false", + category = "generator", + help = "if we generate a package with a few files." + ) + public boolean aFewFiles; + + @Option( + name = "many_files", + defaultValue = "false", + category = "generator", + help = "if we generate a package with many files." + ) + public boolean manyFiles; + + @Option( + name = "long_chained_deps", + defaultValue = "false", + category = "generator", + help = "if we generate a package with long chained dependencies." + ) + public boolean longChainedDeps; + + @Option( + name = "parallel_deps", + defaultValue = "false", + category = "generator", + help = "if we generate a package with parallel dependencies." + ) + public boolean parallelDeps; +} diff --git a/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/JavaCodeGenerator.java b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/JavaCodeGenerator.java new file mode 100644 index 0000000000..f1ba3a6eed --- /dev/null +++ b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/JavaCodeGenerator.java @@ -0,0 +1,230 @@ +// 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.devtools.build.benchmark.codegenerator; + +import com.google.common.annotations.VisibleForTesting; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** Create 4 types of Java project, or modify existing ones. */ +public class JavaCodeGenerator { + + @VisibleForTesting static final String TARGET_A_FEW_FILES = "AFewFiles"; + @VisibleForTesting static final int SIZE_A_FEW_FILES = 10; + + @VisibleForTesting static final String TARGET_MANY_FILES = "ManyFiles"; + @VisibleForTesting static final int SIZE_MANY_FILES = 1000; + + @VisibleForTesting static final String TARGET_LONG_CHAINED_DEPS = "LongChainedDeps"; + @VisibleForTesting static final int SIZE_LONG_CHAINED_DEPS = 20; + + @VisibleForTesting static final String TARGET_PARALLEL_DEPS = "ParallelDeps"; + @VisibleForTesting static final int SIZE_PARALLEL_DEPS = 20; + + public static void generateNewProject( + String outputDir, + boolean aFewFiles, + boolean manyFiles, + boolean longChainedDeps, + boolean parallelDeps) { + Path dir = Paths.get(outputDir); + if (aFewFiles) { + createTargetWithSomeFiles(dir.resolve(TARGET_A_FEW_FILES), SIZE_A_FEW_FILES); + } + if (manyFiles) { + createTargetWithSomeFiles(dir.resolve(TARGET_MANY_FILES), SIZE_MANY_FILES); + } + if (longChainedDeps) { + createTargetWithLongChainedDeps(dir.resolve(TARGET_LONG_CHAINED_DEPS)); + } + if (parallelDeps) { + createTargetWithParallelDeps(dir.resolve(TARGET_PARALLEL_DEPS)); + } + } + + public static void modifyExistingProject( + String outputDir, + boolean aFewFiles, + boolean manyFiles, + boolean longChainedDeps, + boolean parallelDeps) { + Path dir = Paths.get(outputDir); + if (aFewFiles) { + modifyTargetWithSomeFiles(dir.resolve(TARGET_A_FEW_FILES)); + } + if (manyFiles) { + modifyTargetWithSomeFiles(dir.resolve(TARGET_MANY_FILES)); + } + if (longChainedDeps) { + modifyTargetWithLongChainedDeps(dir.resolve(TARGET_LONG_CHAINED_DEPS)); + } + if (parallelDeps) { + modifyTargetWithParallelDeps(dir.resolve(TARGET_PARALLEL_DEPS)); + } + } + + /** Target type 1/2: Create targets with some files */ + private static void createTargetWithSomeFiles(Path projectPath, int numberOfFiles) { + if (pathExists(projectPath)) { + return; + } + + try { + Files.createDirectories(projectPath); + + for (int i = 0; i < numberOfFiles; ++i) { + JavaCodeGeneratorHelper.writeRandomClassToDir( + /* addExtraMethod = */ false, "RandomClass" + i, "com.example.generated", projectPath); + } + + JavaCodeGeneratorHelper.writeMainClassToDir("com.example.generated", projectPath); + JavaCodeGeneratorHelper.buildFileWithMainClass(TARGET_A_FEW_FILES, "", projectPath); + } catch (IOException e) { + System.err.println("Error creating target with some files: " + e.getMessage()); + } + } + + /** Target type 1/2: Modify targets with some files */ + private static void modifyTargetWithSomeFiles(Path projectPath) { + File dir = projectPath.toFile(); + if (directoryExists(dir)) { + System.err.format( + "Project dir (%s) does not contain code for modification.\n", projectPath.toString()); + return; + } + try { + JavaCodeGeneratorHelper.writeRandomClassToDir( + /* addExtraMethod = */ true, "RandomClass0", "com.example.generated", projectPath); + } catch (IOException e) { + System.err.println("Error modifying targets some files: " + e.getMessage()); + } + } + + /** Target type 3: Create targets with a few long chained dependencies (A -> B -> C -> … -> Z) */ + private static void createTargetWithLongChainedDeps(Path projectPath) { + if (pathExists(projectPath)) { + return; + } + + try { + Files.createDirectories(projectPath); + + int count = SIZE_LONG_CHAINED_DEPS; + + // Call next one for 0..(count-2) + for (int i = 0; i < count - 1; ++i) { + JavaCodeGeneratorHelper.targetWithNextHelper(i, true, projectPath); + JavaCodeGeneratorHelper.buildFileWithNextDeps( + i, " deps=[ \":Deps" + (i + 1) + "\" ],\n", projectPath); + } + // Don't call next one for (count-1) + JavaCodeGeneratorHelper.targetWithNextHelper(count - 1, false, projectPath); + JavaCodeGeneratorHelper.buildFileWithNextDeps(count - 1, "", projectPath); + + JavaCodeGeneratorHelper.writeMainClassToDir("com.example.generated", projectPath); + + String deps = " deps=[ \":Deps0\" ],\n"; + JavaCodeGeneratorHelper.buildFileWithMainClass(TARGET_LONG_CHAINED_DEPS, deps, projectPath); + } catch (IOException e) { + System.err.println( + "Error creating targets with a few long chained dependencies: " + e.getMessage()); + } + } + + /** Target type 3: Modify targets with a few long chained dependencies (A -> B -> C -> … -> Z) */ + private static void modifyTargetWithLongChainedDeps(Path projectPath) { + File dir = projectPath.toFile(); + if (directoryExists(dir)) { + System.err.format( + "Project dir (%s) does not contain code for modification.\n", projectPath.toString()); + return; + } + try { + JavaCodeGeneratorHelper.targetWithNextExtraHelper( + (SIZE_LONG_CHAINED_DEPS + 1) >> 1, true, projectPath); + } catch (IOException e) { + System.err.println( + "Error modifying targets with a few long chained dependencies: " + e.getMessage()); + } + } + + /** Target type 4: Create targets with lots of parallel dependencies (A -> B, C, D, E, F, G, H) */ + private static void createTargetWithParallelDeps(Path projectPath) { + if (pathExists(projectPath)) { + return; + } + + try { + Files.createDirectories(projectPath); + + int count = SIZE_PARALLEL_DEPS; + + // parallel dependencies B~Z + for (int i = 1; i < count; ++i) { + JavaCodeGeneratorHelper.writeRandomClassToDir( + false, "Deps" + i, "com.example.deps" + i, projectPath); + JavaCodeGeneratorHelper.buildFileWithNextDeps(i, "", projectPath); + } + + // A(Main) + JavaCodeGeneratorHelper.parallelDepsMainClassHelper(count, projectPath); + + String deps = " deps=[ "; + for (int i = 1; i < count; ++i) { + deps += "\":Deps" + i + "\", "; + } + deps += "], \n"; + JavaCodeGeneratorHelper.buildFileWithMainClass(TARGET_PARALLEL_DEPS, deps, projectPath); + } catch (IOException e) { + System.err.println( + "Error creating targets with lots of parallel dependencies: " + e.getMessage()); + } + } + + /** Target type 4: Modify targets with lots of parallel dependencies (A -> B, C, D, E, F, G, H) */ + private static void modifyTargetWithParallelDeps(Path projectPath) { + File dir = projectPath.toFile(); + if (directoryExists(dir)) { + System.err.format( + "Project dir (%s) does not contain code for modification.\n", projectPath.toString()); + return; + } + try { + JavaCodeGeneratorHelper.writeRandomClassToDir( + true, "Deps1", "com.example.deps1", projectPath); + } catch (IOException e) { + System.err.println( + "Error creating targets with lots of parallel dependencies: " + e.getMessage()); + } + } + + private static boolean pathExists(Path path) { + File dir = path.toFile(); + if (dir.exists()) { + System.err.println("File or directory exists, not rewriting it: " + path); + return true; + } + + return false; + } + + private static boolean directoryExists(File file) { + return !(file.exists() && file.isDirectory()); + } +} diff --git a/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/JavaCodeGeneratorHelper.java b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/JavaCodeGeneratorHelper.java new file mode 100644 index 0000000000..82a0c48c1a --- /dev/null +++ b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/JavaCodeGeneratorHelper.java @@ -0,0 +1,225 @@ +// 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.devtools.build.benchmark.codegenerator; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.base.Joiner; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeSpec; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Random; +import javax.lang.model.element.Modifier; + +/** Helper class of {@link JavaCodeGenerator} */ +class JavaCodeGeneratorHelper { + + private static final Joiner JOINER = Joiner.on("\n"); + private static final MethodSpec randomMethod = genRandomMethod("PrintSth"); + private static final MethodSpec somethingElseMethod = genRandomMethod("PrintSthElse"); + + /** + * Writes a class file {@code Deps(index).java} to the directory + * {@code projectPath/com/example/deps(index)} + * + * @param callNext if we should call the method from {@code Deps(index+1).java} + */ + static void targetWithNextHelper(int index, boolean callNext, Path projectPath) + throws IOException { + ClassName nextClass = ClassName.get("com.example.deps" + (index + 1), "Deps" + (index + 1)); + + MethodSpec callNextMethod = + MethodSpec.methodBuilder("CallNext") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(void.class) + .addStatement("$T.PrintSth()", nextClass) + .build(); + + TypeSpec.Builder klassBuilder = + TypeSpec.classBuilder("Deps" + index) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addMethod(randomMethod); + if (callNext) { + klassBuilder.addMethod(callNextMethod); + } + TypeSpec klass = klassBuilder.build(); + + writeClassToDir(klass, "com.example.deps" + index, projectPath); + } + + /** + * Writes a class file {@code Deps(index).java} with extra method {@code printSthElse()} + * to the directory {@code projectPath/com/example/deps(index)} + * + * @param callNext if we should call the method from {@code Deps(index+1).java} + */ + static void targetWithNextExtraHelper(int index, boolean callNext, Path projectPath) + throws IOException { + ClassName nextClass = ClassName.get("com.example.deps" + (index + 1), "Deps" + (index + 1)); + + MethodSpec callNextMethod = + MethodSpec.methodBuilder("CallNext") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(void.class) + .addStatement("$T.PrintSth()", nextClass) + .build(); + + TypeSpec.Builder klassBuilder = + TypeSpec.classBuilder("Deps" + index) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addMethod(randomMethod) + .addMethod(somethingElseMethod); + if (callNext) { + klassBuilder.addMethod(callNextMethod); + } + TypeSpec klass = klassBuilder.build(); + + writeClassToDir(klass, "com.example.deps" + index, projectPath); + } + + /** + * Writes {@code count-1} class files to the directory {@code projectPath/com/example/deps(index)} + * and one main class. + */ + static void parallelDepsMainClassHelper(int count, Path projectPath) throws IOException { + MethodSpec.Builder callDepsBuilder = + MethodSpec.methodBuilder("main") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addParameter(String[].class, "args") + .returns(void.class); + for (int i = 1; i < count; ++i) { + ClassName callingClass = ClassName.get("com.example.deps" + i, "Deps" + i); + callDepsBuilder.addStatement("$T.PrintSth()", callingClass); + } + MethodSpec callDeps = callDepsBuilder.build(); + TypeSpec klass = + TypeSpec.classBuilder("Main") + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addMethod(callDeps) + .build(); + writeClassToDir(klass, "com.example.generated", projectPath); + } + + static void writeRandomClassToDir( + boolean addExtraMethod, String className, String packageName, Path projectPath) + throws IOException { + TypeSpec klass = genRandomClass(addExtraMethod, className); + writeClassToDir(klass, packageName, projectPath); + } + + static void writeMainClassToDir(String packageName, Path projectPath) throws IOException { + TypeSpec main = genMainClass(); + writeClassToDir(main, packageName, projectPath); + } + + static void buildFileWithNextDeps(int index, String deps, Path projectPath) throws IOException { + Path buildFilePath = projectPath.resolve("BUILD"); + + String buildFileContent = + String.format( + JOINER + .join( + "java_library(", + " name = 'Deps%d',", + " srcs = glob([ 'com/example/deps%d/*.java' ]),", + "%s", + " visibility = [ '//visibility:public' ],", + ")"), + index, + index, + deps); + + createAndAppendFile(buildFilePath, buildFileContent); + } + + static void buildFileWithMainClass(String targetName, String deps, Path projectPath) + throws IOException { + Path buildFilePath = projectPath.resolve("BUILD"); + + String buildFileContent = + String.format( + JOINER + .join( + "java_binary(", + " name = '%s',", + " srcs = glob([ 'com/example/generated/*.java' ]),", + " main_class = 'com.example.generated.Main',", + "%s", + ")"), + targetName, + deps); + + createAndAppendFile(buildFilePath, buildFileContent); + } + + private static MethodSpec genRandomMethod(String methodName) { + return MethodSpec.methodBuilder(methodName) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(void.class) + .addStatement("$T rand = new Random()", Random.class) + .addStatement("int n = rand.nextInt(100)") + .addStatement( + "$T.out.format($S, $S, $L)", + System.class, + "This is method(%s) with random number(%d)\n", + methodName, + "n") + .build(); + } + + private static TypeSpec genRandomClass(boolean addExtraMethod, String className) { + + TypeSpec.Builder klassBuilder = + TypeSpec.classBuilder(className) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addMethod(randomMethod); + + if (addExtraMethod) { + klassBuilder.addMethod(somethingElseMethod); + } + + return klassBuilder.build(); + } + + private static TypeSpec genMainClass() { + MethodSpec method = + MethodSpec.methodBuilder("main") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(void.class) + .addParameter(String[].class, "args") + .build(); + + return TypeSpec.classBuilder("Main").addModifiers(Modifier.PUBLIC).addMethod(method).build(); + } + + private static void writeClassToDir(TypeSpec klass, String packageName, Path path) + throws IOException { + JavaFile javaFile = JavaFile.builder(packageName, klass).build(); + javaFile.writeTo(path); + } + + private static void createAndAppendFile(Path path, String content) throws IOException { + if (!Files.exists(path)) { + Files.createFile(path); + } + Files.write(path, content.getBytes(UTF_8), StandardOpenOption.APPEND); + } + +} diff --git a/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/Main.java b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/Main.java new file mode 100644 index 0000000000..f251701545 --- /dev/null +++ b/src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator/Main.java @@ -0,0 +1,71 @@ +// 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.devtools.build.benchmark.codegenerator; + +import com.google.devtools.common.options.Options; +import com.google.devtools.common.options.OptionsParsingException; +import java.io.File; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** Main class for generating code. */ +public class Main { + + private static final Logger logger = Logger.getLogger(Main.class.getName()); + + public static void main(String[] args) { + GeneratorOptions opt = null; + try { + opt = parseArgs(args); + } catch (Exception e) { + if (!e.getMessage().isEmpty()) { + logger.log(Level.SEVERE, e.getMessage()); + } + System.exit(1); + } + + // Generate or modify Java code + if (opt.modificationMode) { + JavaCodeGenerator.modifyExistingProject( + opt.outputDir, opt.aFewFiles, opt.manyFiles, opt.longChainedDeps, opt.parallelDeps); + } else { + JavaCodeGenerator.generateNewProject( + opt.outputDir, opt.aFewFiles, opt.manyFiles, opt.longChainedDeps, opt.parallelDeps); + } + } + + public static GeneratorOptions parseArgs(String[] args) throws OptionsParsingException { + GeneratorOptions opt = Options.parse(GeneratorOptions.class, args).getOptions(); + + // Check output_dir argument + if (opt.outputDir.isEmpty()) { + throw new IllegalArgumentException("--output_dir should not be empty."); + } + if (opt.modificationMode) { + File dir = new File(opt.outputDir); + if (!(dir.exists() && dir.isDirectory())) { + throw new IllegalArgumentException( + "--output_dir (" + opt.outputDir + ") does not contain code for modification."); + } + } + // Check at least one type of package will be generated + if (!(opt.aFewFiles || opt.manyFiles || opt.longChainedDeps || opt.parallelDeps)) { + System.err.println(Options.getUsage(GeneratorOptions.class)); + throw new IllegalArgumentException("No type of package is specified."); + } + + return opt; + } +} diff --git a/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/BUILD b/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/BUILD new file mode 100644 index 0000000000..6982009058 --- /dev/null +++ b/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/BUILD @@ -0,0 +1,36 @@ +java_test( + name = "JavaCodeGeneratorHelperTest", + srcs = ["JavaCodeGeneratorHelperTest.java"], + deps = [ + "//src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator:codegenerator_lib", + "//third_party:guava", + "//third_party:junit4", + ], +) + +java_test( + name = "JavaCodeGeneratorTest", + srcs = ["JavaCodeGeneratorTest.java"], + deps = [ + "//src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator:codegenerator_lib", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "MainTest", + srcs = ["MainTest.java"], + deps = [ + "//src/main/java/com/google/devtools/common/options", + "//src/tools/benchmark/java/com/google/devtools/build/benchmark/codegenerator:codegenerator_lib", + "//third_party:junit4", + ], +) + +filegroup( + name = "srcs", + srcs = glob(["**"]), + visibility = ["//src:__pkg__"], +) diff --git a/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/JavaCodeGeneratorHelperTest.java b/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/JavaCodeGeneratorHelperTest.java new file mode 100644 index 0000000000..15f41b30a5 --- /dev/null +++ b/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/JavaCodeGeneratorHelperTest.java @@ -0,0 +1,255 @@ +// 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.devtools.build.benchmark.codegenerator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.common.base.Joiner; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Scanner; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link JavaCodeGeneratorHelper}. */ +@RunWith(JUnit4.class) +public class JavaCodeGeneratorHelperTest { + + private static final String RANDOM_CLASS_CONTENT = joinLines( + "package com.package.name;", + "", + "import java.lang.System;", + "import java.util.Random;", + "", + "public final class ClassName {", + " public static void PrintSth() {", + " Random rand = new Random();", + " int n = rand.nextInt(100);", + " System.out.format(\"This is method(%s) with random number(%d)\\n\", \"PrintSth\", n);", + " }", + "}"); + + private static final String RANDOM_CLASS_EXTRA_CONTENT = joinLines( + "package com.package.name;", + "", + "import java.lang.System;", + "import java.util.Random;", + "", + "public final class ClassNameExtra {", + " public static void PrintSth() {", + " Random rand = new Random();", + " int n = rand.nextInt(100);", + " System.out.format(\"This is method(%s) with random number(%d)\\n\", \"PrintSth\", n);", + " }", + "", + " public static void PrintSthElse() {", + " Random rand = new Random();", + " int n = rand.nextInt(100);", + " System.out.format(\"This is method(%s) with random number(%d)\\n\"," + + " \"PrintSthElse\", n);", + " }", + "}"); + + private static final String MAIN_CLASS_CONTENT = joinLines( + "package com.package.name;", + "", + "import java.lang.String;", + "", + "public class Main {", + " public static void main(String[] args) {", + " }", + "}"); + + private static final String DEPS_BUILD_FILE_CONTENT = joinLines( + "java_library(", + " name = 'Deps42',", + " srcs = glob([ 'com/example/deps42/*.java' ]),", + "<this is deps>", + " visibility = [ '//visibility:public' ],", + ")"); + + private static final String TARGET_BUILD_FILE_CONTENT = joinLines( + "java_binary(", + " name = 'Target',", + " srcs = glob([ 'com/example/generated/*.java' ]),", + " main_class = 'com.example.generated.Main',", + "<this is deps>", + ")"); + + private static final String DEPS_CLASS_CONTENT = joinLines( + "package com.example.deps42;", + "", + "import com.example.deps43.Deps43;", + "import java.lang.System;", + "import java.util.Random;", + "", + "public final class Deps42 {", + " public static void PrintSth() {", + " Random rand = new Random();", + " int n = rand.nextInt(100);", + " System.out.format(\"This is method(%s) with random number(%d)\\n\", \"PrintSth\", n);", + " }", + "", + " public static void CallNext() {", + " Deps43.PrintSth();", + " }", + "}"); + + private static final String DEPS_CLASS_EXTRA_CONTENT = joinLines( + "package com.example.deps42;", + "", + "import java.lang.System;", + "import java.util.Random;", + "", + "public final class Deps42 {", + " public static void PrintSth() {", + " Random rand = new Random();", + " int n = rand.nextInt(100);", + " System.out.format(\"This is method(%s) with random number(%d)\\n\", \"PrintSth\", n);", + " }", + "", + " public static void PrintSthElse() {", + " Random rand = new Random();", + " int n = rand.nextInt(100);", + " System.out.format(\"This is method(%s) with random number(%d)\\n\"," + + " \"PrintSthElse\", n);", + " }", + "}"); + + private static final String MAIN_CLASS_WITH_DEPS_CONTENT = joinLines( + "package com.example.generated;", + "", + "import com.example.deps1.Deps1;", + "import com.example.deps2.Deps2;", + "import com.example.deps3.Deps3;", + "import java.lang.String;", + "", + "public final class Main {", + " public static void main(String[] args) {", + " Deps1.PrintSth();", + " Deps2.PrintSth();", + " Deps3.PrintSth();", + " }", + "}"); + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Test + public void testWriteRandomClassToDir() throws IOException { + Path dir = folder.newFolder("WriteRandomClassToDir").toPath(); + JavaCodeGeneratorHelper.writeRandomClassToDir(false, "ClassName", "com.package.name", dir); + + Path javaFile = dir.resolve("com/package/name/ClassName.java"); + assertTrue(javaFile.toFile().exists()); + + String content = new Scanner(javaFile).useDelimiter("\\Z").next(); + assertEquals(RANDOM_CLASS_CONTENT, content); + } + + @Test + public void testWriteRandomClassToDirExtraMethod() throws IOException { + Path dir = folder.newFolder("WriteRandomClassToDirExtraMethod").toPath(); + JavaCodeGeneratorHelper.writeRandomClassToDir(true, "ClassNameExtra", "com.package.name", dir); + + Path javaFile = dir.resolve("com/package/name/ClassNameExtra.java"); + assertTrue(javaFile.toFile().exists()); + + String content = new Scanner(javaFile).useDelimiter("\\Z").next(); + assertEquals(RANDOM_CLASS_EXTRA_CONTENT, content); + } + + @Test + public void testWriteMainClassToDir() throws IOException { + Path dir = folder.newFolder("WriteMainClassToDir").toPath(); + JavaCodeGeneratorHelper.writeMainClassToDir("com.package.name", dir); + + Path javaFile = dir.resolve("com/package/name/Main.java"); + assertTrue(javaFile.toFile().exists()); + + String content = new Scanner(javaFile).useDelimiter("\\Z").next(); + assertEquals(MAIN_CLASS_CONTENT, content); + } + + @Test + public void testBuildFileWithNextDeps() throws IOException { + Path dir = folder.newFolder("BuildFileWithNextDeps").toPath(); + Files.createDirectories(dir); + JavaCodeGeneratorHelper.buildFileWithNextDeps(42, "<this is deps>", dir); + + Path buildFile = dir.resolve("BUILD"); + assertTrue(buildFile.toFile().exists()); + + String content = new Scanner(buildFile).useDelimiter("\\Z").next(); + assertEquals(DEPS_BUILD_FILE_CONTENT, content); + } + + @Test + public void testBuildFileWithMainClass() throws IOException { + Path dir = folder.newFolder("BuildFileWithMainClass").toPath(); + Files.createDirectories(dir); + JavaCodeGeneratorHelper.buildFileWithMainClass("Target", "<this is deps>", dir); + + Path buildFile = dir.resolve("BUILD"); + assertTrue(buildFile.toFile().exists()); + + String content = new Scanner(buildFile).useDelimiter("\\Z").next(); + assertEquals(TARGET_BUILD_FILE_CONTENT, content); + } + + @Test + public void testTargetWithNextHelper() throws IOException { + Path dir = folder.newFolder("TargetWithNextHelper").toPath(); + JavaCodeGeneratorHelper.targetWithNextHelper(42, true, dir); + + Path javaFile = dir.resolve("com/example/deps42/Deps42.java"); + assertTrue(javaFile.toFile().exists()); + + String content = new Scanner(javaFile).useDelimiter("\\Z").next(); + assertEquals(DEPS_CLASS_CONTENT, content); + } + + @Test + public void testTargetWithNextExtraHelper() throws IOException { + Path dir = folder.newFolder("TargetWithNextHelperExtra").toPath(); + JavaCodeGeneratorHelper.targetWithNextExtraHelper(42, false, dir); + + Path javaFile = dir.resolve("com/example/deps42/Deps42.java"); + assertTrue(javaFile.toFile().exists()); + + String content = new Scanner(javaFile).useDelimiter("\\Z").next(); + assertEquals(DEPS_CLASS_EXTRA_CONTENT, content); + } + + @Test + public void testParallelDepsMainClassHelper() throws IOException { + Path dir = folder.newFolder("ParallelDepsMainClassHelper").toPath(); + JavaCodeGeneratorHelper.parallelDepsMainClassHelper(4, dir); + + Path javaFile = dir.resolve("com/example/generated/Main.java"); + assertTrue(javaFile.toFile().exists()); + + String content = new Scanner(javaFile).useDelimiter("\\Z").next(); + assertEquals(MAIN_CLASS_WITH_DEPS_CONTENT, content); + } + + private static String joinLines(String... lines) { + return Joiner.on("\n").join(lines); + } +} diff --git a/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/JavaCodeGeneratorTest.java b/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/JavaCodeGeneratorTest.java new file mode 100644 index 0000000000..f135b1fb77 --- /dev/null +++ b/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/JavaCodeGeneratorTest.java @@ -0,0 +1,124 @@ +// 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.devtools.build.benchmark.codegenerator; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertNotNull; + +import com.google.common.collect.ImmutableSet; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link JavaCodeGenerator}. */ +@RunWith(JUnit4.class) +public class JavaCodeGeneratorTest { + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Test + public void testGenerateNewProject() throws IOException { + File createdFolder = folder.newFolder("GenerateNewProject"); + Path dir = createdFolder.toPath(); + JavaCodeGenerator.generateNewProject(dir.toString(), true, true, true, true); + + // Check dir contains 4 project directories + File[] filesList = dir.toFile().listFiles(); + assertNotNull(filesList); + ImmutableSet<String> filenames = fileArrayToImmutableSet(filesList); + assertThat(filenames).containsExactly( + JavaCodeGenerator.TARGET_A_FEW_FILES, + JavaCodeGenerator.TARGET_LONG_CHAINED_DEPS, + JavaCodeGenerator.TARGET_MANY_FILES, + JavaCodeGenerator.TARGET_PARALLEL_DEPS); + + // Target 1: a few files + checkProjectPathContains(dir, JavaCodeGenerator.TARGET_A_FEW_FILES); + checkSimpleTarget( + dir, JavaCodeGenerator.TARGET_A_FEW_FILES, JavaCodeGenerator.SIZE_A_FEW_FILES); + + // Target 2: many files + checkProjectPathContains(dir, JavaCodeGenerator.TARGET_MANY_FILES); + checkSimpleTarget(dir, JavaCodeGenerator.TARGET_MANY_FILES, JavaCodeGenerator.SIZE_MANY_FILES); + + // Target 3: long chained deps + checkProjectPathContains(dir, JavaCodeGenerator.TARGET_LONG_CHAINED_DEPS); + checkDepsTarget( + dir, JavaCodeGenerator.TARGET_LONG_CHAINED_DEPS, JavaCodeGenerator.SIZE_LONG_CHAINED_DEPS); + + // Target 4: parallel deps + checkProjectPathContains(dir, JavaCodeGenerator.TARGET_PARALLEL_DEPS); + checkDepsTarget( + dir, JavaCodeGenerator.TARGET_PARALLEL_DEPS, JavaCodeGenerator.SIZE_PARALLEL_DEPS); + } + + private static ImmutableSet<String> fileArrayToImmutableSet(File[] files) { + ImmutableSet.Builder<String> builder = ImmutableSet.builder(); + for (File file : files) { + builder.add(file.getName()); + } + return builder.build(); + } + + private static void checkProjectPathContains(Path root, String targetName) { + // Check project dir contains BUILD and com + File[] filesList = root.resolve(targetName).toFile().listFiles(); + assertNotNull(filesList); + ImmutableSet<String> filenames = fileArrayToImmutableSet(filesList); + assertThat(filenames).containsExactly("BUILD", "com"); + + // Check project dir contains com/example + filesList = root.resolve(targetName).resolve("com").toFile().listFiles(); + assertNotNull(filesList); + filenames = fileArrayToImmutableSet(filesList); + assertThat(filenames).containsExactly("example"); + } + + private static void checkSimpleTarget(Path root, String targetName, int targetSize) { + // Check Java files + File[] filesList = + root.resolve(targetName).resolve("com/example/generated").toFile().listFiles(); + assertNotNull(filesList); + ImmutableSet<String> filenames = fileArrayToImmutableSet(filesList); + ImmutableSet.Builder<String> randomClassNames = ImmutableSet.builder(); + randomClassNames.add("Main.java"); + for (int i = 0; i < targetSize; ++i) { + randomClassNames.add("RandomClass" + i + ".java"); + } + assertThat(filenames).containsExactlyElementsIn(randomClassNames.build()); + } + + private static void checkDepsTarget(Path root, String targetName, int targetSize) { + // Check Java files + for (int i = 1; i < targetSize; ++i) { + File[] filesList = + root.resolve(targetName).resolve("com/example/deps" + i).toFile().listFiles(); + assertNotNull(filesList); + ImmutableSet<String> filenames = fileArrayToImmutableSet(filesList); + assertThat(filenames).containsExactly("Deps" + i + ".java"); + } + File[] filesList = + root.resolve(targetName).resolve("com/example/generated").toFile().listFiles(); + assertNotNull(filesList); + ImmutableSet<String> filenames = fileArrayToImmutableSet(filesList); + assertThat(filenames).containsExactly("Main.java"); + } +} diff --git a/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/MainTest.java b/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/MainTest.java new file mode 100644 index 0000000000..ef47492dbb --- /dev/null +++ b/src/tools/benchmark/javatests/com/google/devtools/build/benchmark/codegenerator/MainTest.java @@ -0,0 +1,92 @@ +// 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.devtools.build.benchmark.codegenerator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import com.google.devtools.common.options.OptionsParsingException; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link Main}. */ +@RunWith(JUnit4.class) +public class MainTest { + + @Test + public void testParseArgsEmpty() throws OptionsParsingException, IOException { + try { + Main.parseArgs(new String[]{}); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("--output_dir should not be empty.", e.getMessage()); + } + } + + @Test + public void testParseArgsWrongMode() throws IOException { + try { + Main.parseArgs(new String[]{"--modify=mango"}); + fail("Should throw OptionsParsingException"); + } catch (OptionsParsingException e) { + assertEquals( + "While parsing option --modify=mango: 'mango' is not a boolean", e.getMessage()); + } + } + + @Test + public void testParseArgsNoOutputDir() throws OptionsParsingException, IOException { + try { + Main.parseArgs(new String[]{"--modify"}); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("--output_dir should not be empty.", e.getMessage()); + } + } + + @Test + public void testParseArgsOutputDirNonExists() throws OptionsParsingException, IOException { + try { + Main.parseArgs(new String[]{"--modify", "--output_dir=mango"}); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("--output_dir (mango) does not contain code for modification.", e.getMessage()); + } + } + + @Test + public void testParseArgsNoType() throws OptionsParsingException, IOException { + try { + Main.parseArgs(new String[]{"--output_dir=mango"}); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("No type of package is specified.", e.getMessage()); + } + } + + @Test + public void testParseArgsCorrect() throws OptionsParsingException, IOException { + GeneratorOptions opt = Main.parseArgs( + new String[]{"--modify=false", "--output_dir=mango", "--a_few_files", "--parallel_deps"}); + assertEquals(opt.modificationMode, false); + assertEquals(opt.outputDir, "mango"); + assertEquals(opt.aFewFiles, true); + assertEquals(opt.manyFiles, false); + assertEquals(opt.longChainedDeps, false); + assertEquals(opt.parallelDeps, true); + } +} |