Merge changes I14928b7b,Ia009df3d into main
* changes: Generate runtime stubs in droidstubs Additional cleanup prior to adding the runtime stubs
This commit is contained in:
commit
60bdd05b21
3 changed files with 141 additions and 57 deletions
|
@ -222,6 +222,8 @@ type Javadoc struct {
|
||||||
stubsSrcJar android.WritablePath
|
stubsSrcJar android.WritablePath
|
||||||
|
|
||||||
exportableStubsSrcJar android.WritablePath
|
exportableStubsSrcJar android.WritablePath
|
||||||
|
|
||||||
|
runtimeStubsSrcJar android.WritablePath
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Javadoc) OutputFiles(tag string) (android.Paths, error) {
|
func (j *Javadoc) OutputFiles(tag string) (android.Paths, error) {
|
||||||
|
|
|
@ -214,6 +214,7 @@ type currentApiTimestampProvider interface {
|
||||||
type annotationFlagsParams struct {
|
type annotationFlagsParams struct {
|
||||||
migratingNullability bool
|
migratingNullability bool
|
||||||
validatingNullability bool
|
validatingNullability bool
|
||||||
|
extractAnnotations bool
|
||||||
nullabilityWarningsFile android.WritablePath
|
nullabilityWarningsFile android.WritablePath
|
||||||
annotationsZip android.WritablePath
|
annotationsZip android.WritablePath
|
||||||
}
|
}
|
||||||
|
@ -239,6 +240,9 @@ type stubsCommandConfigParams struct {
|
||||||
writeSdkValues bool
|
writeSdkValues bool
|
||||||
migratingNullability bool
|
migratingNullability bool
|
||||||
validatingNullability bool
|
validatingNullability bool
|
||||||
|
annotationsEnabled bool
|
||||||
|
apiLevelsAnnotationsEnabled bool
|
||||||
|
extractAnnotations bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
|
// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
|
||||||
|
@ -508,7 +512,6 @@ func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params annotationFlagsParams) {
|
func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params annotationFlagsParams) {
|
||||||
if Bool(d.properties.Annotations_enabled) {
|
|
||||||
cmd.Flag(config.MetalavaAnnotationsFlags)
|
cmd.Flag(config.MetalavaAnnotationsFlags)
|
||||||
|
|
||||||
if params.migratingNullability {
|
if params.migratingNullability {
|
||||||
|
@ -524,7 +527,9 @@ func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.Ru
|
||||||
cmd.FlagWithOutput("--nullability-warnings-txt ", params.nullabilityWarningsFile)
|
cmd.FlagWithOutput("--nullability-warnings-txt ", params.nullabilityWarningsFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.extractAnnotations {
|
||||||
cmd.FlagWithOutput("--extract-annotations ", params.annotationsZip)
|
cmd.FlagWithOutput("--extract-annotations ", params.annotationsZip)
|
||||||
|
}
|
||||||
|
|
||||||
if len(d.properties.Merge_annotations_dirs) != 0 {
|
if len(d.properties.Merge_annotations_dirs) != 0 {
|
||||||
d.mergeAnnoDirFlags(ctx, cmd)
|
d.mergeAnnoDirFlags(ctx, cmd)
|
||||||
|
@ -532,7 +537,6 @@ func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.Ru
|
||||||
|
|
||||||
cmd.Flag(config.MetalavaAnnotationsWarningsFlags)
|
cmd.Flag(config.MetalavaAnnotationsWarningsFlags)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
|
func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
|
||||||
ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
|
ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
|
||||||
|
@ -556,9 +560,11 @@ func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *a
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) {
|
func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params stubsCommandParams) {
|
||||||
var apiVersions android.Path
|
var apiVersions android.Path
|
||||||
if proptools.Bool(d.properties.Api_levels_annotations_enabled) {
|
stubsType := params.stubConfig.stubsType
|
||||||
|
apiVersionsXml := params.apiVersionsXml
|
||||||
|
if params.stubConfig.apiLevelsAnnotationsEnabled {
|
||||||
d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml)
|
d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml)
|
||||||
apiVersions = apiVersionsXml
|
apiVersions = apiVersionsXml
|
||||||
} else {
|
} else {
|
||||||
|
@ -569,7 +575,9 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a
|
||||||
} else if stubsType == Exportable {
|
} else if stubsType == Exportable {
|
||||||
apiVersions = s.exportableArtifacts.apiVersionsXml
|
apiVersions = s.exportableArtifacts.apiVersionsXml
|
||||||
} else {
|
} else {
|
||||||
ctx.ModuleErrorf("%s stubs type does not generate api-versions.xml file", stubsType.String())
|
// if the stubs type does not generate api-versions.xml file, default to using the
|
||||||
|
// everything artifacts
|
||||||
|
apiVersions = s.everythingArtifacts.apiVersionsXml
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.PropertyErrorf("api_levels_module",
|
ctx.PropertyErrorf("api_levels_module",
|
||||||
|
@ -803,13 +811,16 @@ func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *andr
|
||||||
annotationParams := annotationFlagsParams{
|
annotationParams := annotationFlagsParams{
|
||||||
migratingNullability: params.stubConfig.migratingNullability,
|
migratingNullability: params.stubConfig.migratingNullability,
|
||||||
validatingNullability: params.stubConfig.validatingNullability,
|
validatingNullability: params.stubConfig.validatingNullability,
|
||||||
|
extractAnnotations: params.stubConfig.extractAnnotations,
|
||||||
nullabilityWarningsFile: params.nullabilityWarningsFile,
|
nullabilityWarningsFile: params.nullabilityWarningsFile,
|
||||||
annotationsZip: params.annotationsZip,
|
annotationsZip: params.annotationsZip,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.stubConfig.annotationsEnabled {
|
||||||
d.annotationsFlags(ctx, cmd, annotationParams)
|
d.annotationsFlags(ctx, cmd, annotationParams)
|
||||||
|
}
|
||||||
d.inclusionAnnotationsFlags(ctx, cmd)
|
d.inclusionAnnotationsFlags(ctx, cmd)
|
||||||
d.apiLevelsAnnotationsFlags(ctx, cmd, params.stubConfig.stubsType, params.apiVersionsXml)
|
d.apiLevelsAnnotationsFlags(ctx, cmd, params)
|
||||||
|
|
||||||
d.expandArgs(ctx, cmd)
|
d.expandArgs(ctx, cmd)
|
||||||
|
|
||||||
|
@ -839,13 +850,13 @@ func (d *Droidstubs) everythingStubCmd(ctx android.ModuleContext, params stubsCo
|
||||||
d.everythingArtifacts.metadataZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-metadata.zip")
|
d.everythingArtifacts.metadataZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-metadata.zip")
|
||||||
}
|
}
|
||||||
|
|
||||||
if Bool(d.properties.Annotations_enabled) {
|
if params.annotationsEnabled {
|
||||||
if params.validatingNullability {
|
if params.validatingNullability {
|
||||||
d.everythingArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_nullability_warnings.txt")
|
d.everythingArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_nullability_warnings.txt")
|
||||||
}
|
}
|
||||||
d.everythingArtifacts.annotationsZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_annotations.zip")
|
d.everythingArtifacts.annotationsZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_annotations.zip")
|
||||||
}
|
}
|
||||||
if Bool(d.properties.Api_levels_annotations_enabled) {
|
if params.apiLevelsAnnotationsEnabled {
|
||||||
d.everythingArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, Everything.String(), "api-versions.xml")
|
d.everythingArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, Everything.String(), "api-versions.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1023,7 +1034,7 @@ func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCo
|
||||||
optionalCmdParams.metadataDir = d.exportableArtifacts.metadataDir
|
optionalCmdParams.metadataDir = d.exportableArtifacts.metadataDir
|
||||||
}
|
}
|
||||||
|
|
||||||
if Bool(d.properties.Annotations_enabled) {
|
if params.annotationsEnabled {
|
||||||
if params.validatingNullability {
|
if params.validatingNullability {
|
||||||
d.exportableArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_nullability_warnings.txt")
|
d.exportableArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_nullability_warnings.txt")
|
||||||
optionalCmdParams.nullabilityWarningsFile = d.exportableArtifacts.nullabilityWarningsFile
|
optionalCmdParams.nullabilityWarningsFile = d.exportableArtifacts.nullabilityWarningsFile
|
||||||
|
@ -1031,7 +1042,7 @@ func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCo
|
||||||
d.exportableArtifacts.annotationsZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_annotations.zip")
|
d.exportableArtifacts.annotationsZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_annotations.zip")
|
||||||
optionalCmdParams.annotationsZip = d.exportableArtifacts.annotationsZip
|
optionalCmdParams.annotationsZip = d.exportableArtifacts.annotationsZip
|
||||||
}
|
}
|
||||||
if Bool(d.properties.Api_levels_annotations_enabled) {
|
if params.apiLevelsAnnotationsEnabled {
|
||||||
d.exportableArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, params.stubsType.String(), "api-versions.xml")
|
d.exportableArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, params.stubsType.String(), "api-versions.xml")
|
||||||
optionalCmdParams.apiVersionsXml = d.exportableArtifacts.apiVersionsXml
|
optionalCmdParams.apiVersionsXml = d.exportableArtifacts.apiVersionsXml
|
||||||
}
|
}
|
||||||
|
@ -1049,6 +1060,38 @@ func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCo
|
||||||
d.optionalStubCmd(ctx, optionalCmdParams)
|
d.optionalStubCmd(ctx, optionalCmdParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sandbox rule for generating runtime stubs
|
||||||
|
func (d *Droidstubs) runtimeStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) {
|
||||||
|
|
||||||
|
// We are only interested in generating the stubs srcjar,
|
||||||
|
// not other artifacts for the runtime stubs
|
||||||
|
params.checkApi = false
|
||||||
|
params.writeSdkValues = false
|
||||||
|
params.validatingNullability = false
|
||||||
|
params.extractAnnotations = false
|
||||||
|
params.apiLevelsAnnotationsEnabled = false
|
||||||
|
|
||||||
|
optionalCmdParams := stubsCommandParams{
|
||||||
|
stubConfig: params,
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Javadoc.runtimeStubsSrcJar = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-"+"stubs.srcjar")
|
||||||
|
optionalCmdParams.stubsSrcJar = d.Javadoc.runtimeStubsSrcJar
|
||||||
|
|
||||||
|
// If aconfig_declarations property is not defined, all flagged apis symbols are stripped
|
||||||
|
// as no aconfig flags are enabled. In such case, the runtime stubs are identical to the
|
||||||
|
// exportable stubs, thus no additional metalava invocation is needed.
|
||||||
|
if len(d.properties.Aconfig_declarations) == 0 {
|
||||||
|
rule := android.NewRuleBuilder(pctx, ctx)
|
||||||
|
rule.Command().
|
||||||
|
Text("cp").Flag("-f").
|
||||||
|
Input(d.exportableStubsSrcJar).Output(d.runtimeStubsSrcJar)
|
||||||
|
rule.Build(fmt.Sprintf("metalava_%s", params.stubsType.String()), "metalava merged")
|
||||||
|
} else {
|
||||||
|
d.optionalStubCmd(ctx, optionalCmdParams)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Droidstubs) optionalStubCmd(ctx android.ModuleContext, params stubsCommandParams) {
|
func (d *Droidstubs) optionalStubCmd(ctx android.ModuleContext, params stubsCommandParams) {
|
||||||
|
|
||||||
params.srcJarDir = android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "srcjars")
|
params.srcJarDir = android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "srcjars")
|
||||||
|
@ -1120,6 +1163,8 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||||
|
|
||||||
annotationsEnabled := Bool(d.properties.Annotations_enabled)
|
annotationsEnabled := Bool(d.properties.Annotations_enabled)
|
||||||
|
|
||||||
|
extractAnnotations := annotationsEnabled
|
||||||
|
|
||||||
migratingNullability := annotationsEnabled && String(d.properties.Previous_api) != ""
|
migratingNullability := annotationsEnabled && String(d.properties.Previous_api) != ""
|
||||||
validatingNullability := annotationsEnabled && (strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
|
validatingNullability := annotationsEnabled && (strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
|
||||||
String(d.properties.Validate_nullability_from_list) != "")
|
String(d.properties.Validate_nullability_from_list) != "")
|
||||||
|
@ -1127,6 +1172,8 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||||
checkApi := apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
|
checkApi := apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
|
||||||
apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released")
|
apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released")
|
||||||
|
|
||||||
|
apiLevelsAnnotationsEnabled := proptools.Bool(d.properties.Api_levels_annotations_enabled)
|
||||||
|
|
||||||
stubCmdParams := stubsCommandConfigParams{
|
stubCmdParams := stubsCommandConfigParams{
|
||||||
javaVersion: javaVersion,
|
javaVersion: javaVersion,
|
||||||
deps: deps,
|
deps: deps,
|
||||||
|
@ -1137,17 +1184,28 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||||
writeSdkValues: writeSdkValues,
|
writeSdkValues: writeSdkValues,
|
||||||
migratingNullability: migratingNullability,
|
migratingNullability: migratingNullability,
|
||||||
validatingNullability: validatingNullability,
|
validatingNullability: validatingNullability,
|
||||||
|
annotationsEnabled: annotationsEnabled,
|
||||||
|
apiLevelsAnnotationsEnabled: apiLevelsAnnotationsEnabled,
|
||||||
|
extractAnnotations: extractAnnotations,
|
||||||
}
|
}
|
||||||
stubCmdParams.stubsType = Everything
|
stubCmdParams.stubsType = Everything
|
||||||
// Create default (i.e. "everything" stubs) rule for metalava
|
// Create default (i.e. "everything" stubs) rule for metalava
|
||||||
d.everythingStubCmd(ctx, stubCmdParams)
|
d.everythingStubCmd(ctx, stubCmdParams)
|
||||||
|
|
||||||
// The module generates "exportable" (and "runtime" eventually) stubs regardless of whether
|
// The module generates "exportable" stubs regardless of whether
|
||||||
// aconfig_declarations property is defined or not. If the property is not defined, the module simply
|
// aconfig_declarations property is defined or not. If the property is not defined, the module simply
|
||||||
// strips all flagged apis to generate the "exportable" stubs
|
// strips all flagged apis to generate the "exportable" stubs
|
||||||
stubCmdParams.stubsType = Exportable
|
stubCmdParams.stubsType = Exportable
|
||||||
d.exportableStubCmd(ctx, stubCmdParams)
|
d.exportableStubCmd(ctx, stubCmdParams)
|
||||||
|
|
||||||
|
// "runtime" stubs do not generate any other artifacts than the stubs.
|
||||||
|
// Therefore, metalava does not have to run for "runtime" configuration
|
||||||
|
// when the module does not generate stubs.
|
||||||
|
if stubCmdParams.generateStubs {
|
||||||
|
stubCmdParams.stubsType = Runtime
|
||||||
|
d.runtimeStubCmd(ctx, stubCmdParams)
|
||||||
|
}
|
||||||
|
|
||||||
if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
|
if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
|
||||||
|
|
||||||
if len(d.Javadoc.properties.Out) > 0 {
|
if len(d.Javadoc.properties.Out) > 0 {
|
||||||
|
|
|
@ -396,23 +396,47 @@ func TestAconfigDeclarations(t *testing.T) {
|
||||||
"bar",
|
"bar",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
droidstubs {
|
||||||
|
name: "baz",
|
||||||
|
srcs: ["a/A.java"],
|
||||||
|
api_surface: "public",
|
||||||
|
check_api: {
|
||||||
|
current: {
|
||||||
|
api_file: "a/current.txt",
|
||||||
|
removed_api_file: "a/removed.txt",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// Check that droidstubs depend on aconfig_declarations
|
// Check that droidstubs depend on aconfig_declarations
|
||||||
android.AssertBoolEquals(t, "foo expected to depend on bar",
|
android.AssertBoolEquals(t, "foo expected to depend on bar",
|
||||||
CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true)
|
CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true)
|
||||||
|
|
||||||
m := result.ModuleForTests("foo", "android_common")
|
fooModule := result.ModuleForTests("foo", "android_common")
|
||||||
android.AssertStringDoesContain(t, "foo generates revert annotations file",
|
android.AssertStringDoesContain(t, "foo generates revert annotations file",
|
||||||
strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt")
|
strings.Join(fooModule.AllOutputs(), ""), "revert-annotations-exportable.txt")
|
||||||
|
|
||||||
// revert-annotations.txt passed to exportable stubs generation metalava command
|
// revert-annotations.txt passed to exportable stubs generation metalava command
|
||||||
manifest := m.Output("metalava_exportable.sbox.textproto")
|
exportableManifest := fooModule.Output("metalava_exportable.sbox.textproto")
|
||||||
cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
|
exportableCmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, exportableManifest).Commands[0].Command)
|
||||||
android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt")
|
android.AssertStringDoesContain(t, "flagged api hide command not included", exportableCmdline, "revert-annotations-exportable.txt")
|
||||||
|
|
||||||
android.AssertStringDoesContain(t, "foo generates exportable stubs jar",
|
android.AssertStringDoesContain(t, "foo generates exportable stubs jar",
|
||||||
strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar")
|
strings.Join(fooModule.AllOutputs(), ""), "exportable/foo-stubs.srcjar")
|
||||||
|
|
||||||
|
// revert-annotations.txt passed to runtime stubs generation metalava command
|
||||||
|
runtimeManifest := fooModule.Output("metalava_runtime.sbox.textproto")
|
||||||
|
runtimeCmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, runtimeManifest).Commands[0].Command)
|
||||||
|
android.AssertStringDoesContain(t, "flagged api hide command not included", runtimeCmdline, "revert-annotations-runtime.txt")
|
||||||
|
|
||||||
|
android.AssertStringDoesContain(t, "foo generates runtime stubs jar",
|
||||||
|
strings.Join(fooModule.AllOutputs(), ""), "runtime/foo-stubs.srcjar")
|
||||||
|
|
||||||
|
// If aconfig_declarations property is not defined, the runtime stubs is a copy of the exportable stubs
|
||||||
|
bazModule := result.ModuleForTests("baz", "android_common")
|
||||||
|
bazRuntimeCmdline := bazModule.Rule("metalava_runtime").RuleParams.Command
|
||||||
|
android.AssertStringDoesContain(t, "copy command should include the input stub", bazRuntimeCmdline, "exportable/baz-stubs.srcjar")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReleaseExportRuntimeApis(t *testing.T) {
|
func TestReleaseExportRuntimeApis(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue