-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ggj][infra][3/5] feat: implement update golden bazel rules for dummy…
… test (#314) * add fileDiffUtils * add new dummy unit test * format * add assertUtils * format * move dummy test to separate folder * add compare strings method in diffUtils * format * dummy test pass * reformat * add simple strings comparison * add java_diff_test bzl * group test framework helpers together * run testRunner * format * run single JUnit test and save output * add copy of golden * format * working pipeline * format * clean up * get codegen in local_tmp * format * add golden path to bazel rules * working pipeline * format * work! * clean up * fix * feedback * clean * comment * bazel rules feedback * java code feedback * move dependency * clean up * comment * move hamcrest dep back * add helpers in Utils
- Loading branch information
1 parent
b6c136c
commit 26611d7
Showing
8 changed files
with
280 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
def _junit_output_impl(ctx): | ||
test_class_name = ctx.attr.test_class_name | ||
inputs = ctx.files.srcs | ||
output = ctx.outputs.output | ||
test_runner = ctx.executable.test_runner | ||
|
||
command = """ | ||
mkdir local_tmp | ||
TEST_OUTPUT_HOME="$(pwd)/local_tmp" \ | ||
{test_runner_path} $@ | ||
cd local_tmp | ||
# Zip all files under local_tmp with all nested parent folders except for local_tmp itself. | ||
# Zip files because there are cases that one Junit test can produce multiple goldens. | ||
zip -r ../{output} . | ||
""".format( | ||
test_runner_path = test_runner.path, | ||
output=output.path, | ||
) | ||
|
||
ctx.actions.run_shell( | ||
inputs = inputs, | ||
outputs = [output], | ||
arguments = [test_class_name], | ||
tools = [test_runner], | ||
command = command, | ||
) | ||
|
||
junit_output_zip = rule( | ||
attrs = { | ||
"test_class_name": attr.string(mandatory=True), | ||
"srcs": attr.label_list( | ||
allow_files = True, | ||
mandatory = True, | ||
), | ||
"test_runner": attr.label( | ||
mandatory = True, | ||
executable = True, | ||
cfg = "host", | ||
), | ||
}, | ||
outputs = { | ||
"output": "%{name}%.zip", | ||
}, | ||
implementation = _junit_output_impl, | ||
) | ||
|
||
def _overwritten_golden_impl(ctx): | ||
script_content = """ | ||
#!/bin/bash | ||
cd ${{BUILD_WORKSPACE_DIRECTORY}} | ||
unzip -ao {unit_test_results} -d src/test/java | ||
""".format( | ||
unit_test_results = ctx.file.unit_test_results.path, | ||
) | ||
ctx.actions.write( | ||
output = ctx.outputs.bin, | ||
content = script_content, | ||
is_executable = True, | ||
) | ||
return [DefaultInfo(executable = ctx.outputs.bin)] | ||
|
||
|
||
overwritten_golden = rule( | ||
attrs = { | ||
"unit_test_results": attr.label( | ||
mandatory = True, | ||
allow_single_file = True), | ||
}, | ||
outputs = { | ||
"bin": "%{name}.sh", | ||
}, | ||
executable = True, | ||
implementation = _overwritten_golden_impl, | ||
) | ||
|
||
def updated_golden(name, test_class_name, srcs): | ||
junit_output_name = "%s_output" % name | ||
junit_output_zip( | ||
name = junit_output_name, | ||
test_class_name = test_class_name, | ||
test_runner = "//:junit_runner", | ||
srcs = srcs, | ||
) | ||
overwritten_golden( | ||
name = name, | ||
unit_test_results = ":%s" % junit_output_name | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
src/test/java/com/google/api/generator/test/framework/SingleJUnitTestRunner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Copyright 2020 Google LLC | ||
// | ||
// 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.api.generator.test.framework; | ||
|
||
import org.junit.runner.JUnitCore; | ||
import org.junit.runner.Request; | ||
import org.junit.runner.Result; | ||
|
||
public class SingleJUnitTestRunner { | ||
// SingleJUnitTestRunner runs single JUnit test whose class name is passed through `args`. | ||
// This is used to prepare codegen for updating goldens files. | ||
public static void main(String... args) { | ||
// Check whether the test class name is passed correctly e.g. | ||
// `com.google.api.generator.gapic.composer.ComposerTest` | ||
if (args.length < 1) { | ||
throw new MissingRequiredArgException("Missing the JUnit class name argument."); | ||
} | ||
String className = args[0]; | ||
Class clazz = null; | ||
try { | ||
clazz = Class.forName(className); | ||
} catch (ClassNotFoundException e) { | ||
throw new JUnitClassNotFoundException( | ||
String.format("JUnit test class %s is not found.", className)); | ||
} | ||
Result result = new JUnitCore().run(Request.aClass(clazz)); | ||
if (!result.wasSuccessful()) { | ||
System.out.println("Tests have failures: " + result.getFailures()); | ||
} | ||
} | ||
|
||
public static class JUnitClassNotFoundException extends RuntimeException { | ||
public JUnitClassNotFoundException(String errorMessage) { | ||
super(errorMessage); | ||
} | ||
|
||
public JUnitClassNotFoundException(String errorMessage, Throwable cause) { | ||
super(errorMessage, cause); | ||
} | ||
} | ||
|
||
public static class MissingRequiredArgException extends RuntimeException { | ||
public MissingRequiredArgException(String errorMessage) { | ||
super(errorMessage); | ||
} | ||
|
||
public MissingRequiredArgException(String errorMessage, Throwable cause) { | ||
super(errorMessage, cause); | ||
} | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
src/test/java/com/google/api/generator/test/framework/Utils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// Copyright 2020 Google LLC | ||
// | ||
// 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.api.generator.test.framework; | ||
|
||
import java.io.FileWriter; | ||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
|
||
public class Utils { | ||
/** | ||
* Save the generated code from JUnit test to a file for updating goldens. These files will be | ||
* saved as a zip file, then unzipped to overwrite goldens files. The relative path | ||
* `com/google/..` which is identical with the location of goldens files which will help us easily | ||
* replace the original goldens. For example: | ||
* `src/test/java/com/google/api/generator/gapic/composer/ComposerTest.java` will save the | ||
* generated code into a file called `ComposerTest.golden` at | ||
* `$TEST_OUTPUT_HOME/com/google/api/generator/gapic/composer/goldens/ComposerTest.golden`. | ||
* | ||
* @param clazz the test class. | ||
* @param fileName the name of saved file, usually it is test method name with suffix `.golden`. | ||
* @param codegen the generated code from JUnit test. | ||
*/ | ||
public static void saveCodegenToFile(Class clazz, String fileName, String codegen) { | ||
// This system environment variable `TEST_OUTPUT_HOME` is used to specify a folder | ||
// which contains generated output from JUnit test. | ||
// It will be set when running `bazel run testTarget_update` command. | ||
String testOutputHome = System.getenv("TEST_OUTPUT_HOME"); | ||
String relativeGoldenDir = getTestoutGoldenDir(clazz); | ||
Path testOutputDir = Paths.get(testOutputHome, relativeGoldenDir); | ||
testOutputDir.toFile().mkdirs(); | ||
try (FileWriter myWriter = | ||
new FileWriter(Paths.get(testOutputHome, relativeGoldenDir, fileName).toFile())) { | ||
myWriter.write(codegen); | ||
} catch (IOException e) { | ||
throw new SaveCodegenToFileException( | ||
String.format( | ||
"Error occured when saving codegen to file %s/%s", relativeGoldenDir, fileName)); | ||
} | ||
} | ||
|
||
private static String getTestoutGoldenDir(Class clazz) { | ||
return clazz.getPackage().getName().replace(".", "/") + "/goldens/"; | ||
} | ||
|
||
public static String getGoldenDir(Class clazz) { | ||
return "src/test/java/" + clazz.getPackage().getName().replace(".", "/") + "/goldens/"; | ||
} | ||
|
||
public static String getClassName(Class clazz) { | ||
return clazz.getSimpleName(); | ||
} | ||
|
||
public static class SaveCodegenToFileException extends RuntimeException { | ||
public SaveCodegenToFileException(String errorMessage) { | ||
super(errorMessage); | ||
} | ||
|
||
public SaveCodegenToFileException(String errorMessage, Throwable cause) { | ||
super(errorMessage, cause); | ||
} | ||
} | ||
} |