diff --git a/.secrets.baseline b/.secrets.baseline index 3d0ba2c73..ee076f4eb 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -2033,7 +2033,7 @@ "hashed_secret": "c042bdfc4bc5516ec716afe9e85c173b614ff9f5", "is_secret": false, "is_verified": false, - "line_number": 835, + "line_number": 900, "type": "Hex High Entropy String", "verified_result": null } diff --git a/docs/content/releases/posts/v0.46.0.md b/docs/content/releases/posts/v0.46.0.md index 3fa1c5aa4..4637863ce 100644 --- a/docs/content/releases/posts/v0.46.0.md +++ b/docs/content/releases/posts/v0.46.0.md @@ -22,6 +22,8 @@ links: - The user can also be set when submitting tests with the Runs API. [See the REST API reference](../../docs/reference/rest-api/index.html). - Runs can now be queried by user in the `galasactl runs get` command with the `--user` flag. This will return any runs where the `user` or `requestor` matches the value passed in with the `--user` flag. [See the command reference](../../docs/reference/cli-syntax/galasactl_runs_get.md). -- Added `galasactl tags` commands to the Galasa CLI tool that can be used to create, read, update, and delete test tags as resources on the Galasa service. [See the command reference for more details](../../docs/reference/cli-syntax/galasactl_tags.md) +- Added `galasactl tags` commands to the Galasa CLI tool that can be used to create, read, update, and delete test tags as resources on the Galasa service. [See the command reference for more details](../../docs/reference/cli-syntax/galasactl_tags.md). - Tags can be assigned a `priority` modifier that can affect the order in which tests submitted to a Galasa service are scheduled. Test submitted with tags that have higher priority modifiers will be scheduled before tests that have no tags or tags with lower priority modifiers. - Tags can also be assigned a `description` which can be used to provide more information about the tag. + +- `galasactl runs submit local` commands can now be supplied with one or more `--methods` flags to run a selection of test methods locally from a Galasa test class that is provided with the `--class` flag. [See the command reference for more details](../../docs/reference/cli-syntax/galasactl_runs_submit_local.md). diff --git a/modules/cli/README.md b/modules/cli/README.md index ff22a1905..cb7c5fe91 100644 --- a/modules/cli/README.md +++ b/modules/cli/README.md @@ -312,14 +312,27 @@ such as cleaning up resources when things fail and arbitrating contention for li between competing tests. It should only be used during test development to verify that the test is behaving correctly. -### Example : Run a single test in the local JVM. +### Example: Run a single test class in the local JVM. ``` galasactl runs submit local --log - --obr mvn:dev.galasa.example.banking/dev.galasa.example.banking.obr/0.0.1-SNAPSHOT/obr --class dev.galasa.example.banking.account/dev.galasa.example.banking.account.TestAccount ``` -### Example : Run a single Gherkin test in the local JVM. +### Example: Run a single test method in the local JVM. + +To run a single test method within a test class, supply the `--methods` flag into the `galasactl runs submit local` command. + +For example, to run the `testCreateAccount` method within the `TestAccount` class, use the following command: + +``` +galasactl runs submit local --log - + --obr mvn:dev.galasa.example.banking/dev.galasa.example.banking.obr/0.0.1-SNAPSHOT/obr + --class dev.galasa.example.banking.account/dev.galasa.example.banking.account.TestAccount + --methods testCreateAccount +``` + +### Example: Run a single Gherkin test in the local JVM. ``` galasactl runs submit local --log - --gherkin file:///path/to/gherkin/file.feature diff --git a/modules/cli/docs/generated/errors-list.md b/modules/cli/docs/generated/errors-list.md index 9a96ab077..2cc6582a2 100644 --- a/modules/cli/docs/generated/errors-list.md +++ b/modules/cli/docs/generated/errors-list.md @@ -273,6 +273,10 @@ The `galasactl` tool can generate the following errors: - GAL1271E: Failed to get tags from the Galasa service. Unexpected http status code {} received from the server. Error details from the server are not in the json format. - GAL1272E: Failed to get tags from the Galasa service. Sending the put request to the Galasa service failed. Cause is {} - GAL1273E: User '{}' specified with the --user flag does not have permission to launch tests. +- GAL1274E: Invalid Java method name. Method name should not be blank. Use the --help flag for more information, or refer to the documentation at https://galasa.dev/docs/reference +- GAL1275E: Invalid Java method name '{}' should start with a letter (a-z, A-Z), not '{}'. Use the --help flag for more information, or refer to the documentation at https://galasa.dev/docs/reference +- GAL1276E: Invalid Java method name '{}' should not contain the '{}' character. Use the --help flag for more information, or refer to the documentation at https://galasa.dev/docs/reference +- GAL1277E: Invalid Java method name. Method name '{}' is a reserved Java keyword. Use the --help flag for more information, or refer to the documentation at https://galasa.dev/docs/reference - GAL2000W: Warning: Maven configuration file settings.xml should contain a reference to a Galasa repository so that the galasa OBR can be resolved. The official release repository is '{}', and 'pre-release' repository is '{}' - GAL2501I: Downloaded {} artifacts to folder '{}' diff --git a/modules/cli/docs/generated/galasactl_runs_submit_local.md b/modules/cli/docs/generated/galasactl_runs_submit_local.md index 32e4cbbb0..3ee1e9410 100644 --- a/modules/cli/docs/generated/galasactl_runs_submit_local.md +++ b/modules/cli/docs/generated/galasactl_runs_submit_local.md @@ -21,6 +21,7 @@ galasactl runs submit local [flags] --gherkin strings Gherkin feature file URL. Should start with 'file://'. -h, --help Displays the options for the 'runs submit local' command. --localMaven string The url of a local maven repository are where galasa bundles can be loaded from on your local file system. Defaults to your home .m2/repository file. Please note that this should be in a URL form e.g. 'file:///Users/myuserid/.m2/repository', or 'file://C:/Users/myuserid/.m2/repository' + --methods strings Optional. The names of the Java test methods from the test class provided via the --class option that you wish to run. Method names must start with a letter (a-z, A-Z), can contain letters, numbers, and underscores, and must not be a Java reserved keyword. If not specified, all methods in the given class are run. Method names are case sensitive. This option can only be used alongside --class and cannot be used with --gherkin. --obr strings The maven coordinates of the obr bundle(s) which refer to your test bundles. The format of this parameter is 'mvn:${TEST_OBR_GROUP_ID}/${TEST_OBR_ARTIFACT_ID}/${TEST_OBR_VERSION}/obr' Multiple instances of this flag can be used to describe multiple obr bundles. --remoteMaven string the url of the remote maven where galasa bundles can be loaded from. Defaults to maven central. (default "https://repo.maven.apache.org/maven2") ``` diff --git a/modules/cli/pkg/cmd/runsSubmitLocal.go b/modules/cli/pkg/cmd/runsSubmitLocal.go index 1d819a71b..64bd92970 100644 --- a/modules/cli/pkg/cmd/runsSubmitLocal.go +++ b/modules/cli/pkg/cmd/runsSubmitLocal.go @@ -139,6 +139,12 @@ func (cmd *RunsSubmitLocalCommand) createRunsSubmitLocalCobraCmd( "The connection is established using the --debugMode and --debugPort values.", ) + runsSubmitLocalCobraCmd.Flags().StringSliceVar(&cmd.values.runsSubmitLocalCmdParams.TestMethods, "methods", make([]string, 0), + "Optional. The names of the Java test methods from the test class provided via the --class option that you wish to run."+ + " Method names must start with a letter (a-z, A-Z), can contain letters, numbers, and underscores, and must not be a Java reserved keyword."+ + " If not specified, all methods in the given class are run. Method names are case sensitive."+ + " This option can only be used alongside --class and cannot be used with --gherkin.") + runs.AddClassFlag(runsSubmitLocalCobraCmd, cmd.values.submitLocalSelectionFlags, false, "test class names."+ " The format of each entry is osgi-bundle-name/java-class-name. Java class names are fully qualified. No .class suffix is needed.") @@ -146,6 +152,7 @@ func (cmd *RunsSubmitLocalCommand) createRunsSubmitLocalCobraCmd( runsSubmitLocalCobraCmd.MarkFlagsRequiredTogether("class", "obr") runsSubmitLocalCobraCmd.MarkFlagsOneRequired("class", "gherkin") + runsSubmitLocalCobraCmd.MarkFlagsMutuallyExclusive("methods", "gherkin") runsSubmitCmd.CobraCommand().AddCommand(runsSubmitLocalCobraCmd) diff --git a/modules/cli/pkg/cmd/runsSubmitLocal_test.go b/modules/cli/pkg/cmd/runsSubmitLocal_test.go index 4ea884426..ee7c7c83c 100644 --- a/modules/cli/pkg/cmd/runsSubmitLocal_test.go +++ b/modules/cli/pkg/cmd/runsSubmitLocal_test.go @@ -198,6 +198,22 @@ func TestRunsSubmitLocalGalasaVersionFlagReturnsOk(t *testing.T) { assert.Contains(t, cmd.Values().(*RunsSubmitLocalCmdValues).runsSubmitLocalCmdParams.TargetGalasaVersion, "0.1.0") } +func TestRunsSubmitLocalWithGherkinAndMethodsFlagsErrors(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, cmd := setupTestCommandCollection(COMMAND_NAME_RUNS_SUBMIT_LOCAL, factory, t) + + var args []string = []string{"runs", "submit", "local", "--gherkin", "my.gherkin.feature", "--methods", "mymethod"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.NotNil(t, err) + assert.ErrorContains(t, err, "if any flags in the group [methods gherkin] are set none of the others can be") + assert.Contains(t, cmd.Values().(*RunsSubmitLocalCmdValues).runsSubmitLocalCmdParams.TestMethods, "mymethod") +} + func TestRunsSubmitLocalLocalMavenFlagReturnsOk(t *testing.T) { // Given... factory := utils.NewMockFactory() diff --git a/modules/cli/pkg/errors/errorMessage.go b/modules/cli/pkg/errors/errorMessage.go index baa841686..31a0a0fd5 100644 --- a/modules/cli/pkg/errors/errorMessage.go +++ b/modules/cli/pkg/errors/errorMessage.go @@ -492,6 +492,12 @@ var ( GALASA_ERROR_GET_TAGS_EXPLANATION_NOT_JSON = NewMessageType("GAL1271E: Failed to get tags from the Galasa service. Unexpected http status code %v received from the server. Error details from the server are not in the json format.", 1271, STACK_TRACE_NOT_WANTED) GALASA_ERROR_GET_TAGS_REQUEST_FAILED = NewMessageType("GAL1272E: Failed to get tags from the Galasa service. Sending the put request to the Galasa service failed. Cause is %v", 1272, STACK_TRACE_NOT_WANTED) + // Java method name validation errors + GALASA_ERROR_METHOD_NAME_BLANK = NewMessageType("GAL1274E: Invalid Java method name. Method name should not be blank."+SEE_COMMAND_REFERENCE, 1274, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_INVALID_FIRST_CHAR_IN_METHOD_NAME = NewMessageType("GAL1275E: Invalid Java method name '%s' should start with a letter (a-z, A-Z), not '%s'."+SEE_COMMAND_REFERENCE, 1275, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_INVALID_CHAR_IN_METHOD_NAME = NewMessageType("GAL1276E: Invalid Java method name '%s' should not contain the '%s' character."+SEE_COMMAND_REFERENCE, 1276, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_INVALID_METHOD_RESERVED_WORD = NewMessageType("GAL1277E: Invalid Java method name. Method name '%s' is a reserved Java keyword."+SEE_COMMAND_REFERENCE, 1277, STACK_TRACE_NOT_WANTED) + // Warnings... GALASA_WARNING_MAVEN_NO_GALASA_OBR_REPO = NewMessageType("GAL2000W: Warning: Maven configuration file settings.xml should contain a reference to a Galasa repository so that the galasa OBR can be resolved. The official release repository is '%s', and 'pre-release' repository is '%s'", 2000, STACK_TRACE_WANTED) @@ -509,5 +515,5 @@ var ( // >>> Unit tests guarantee that this number is 'free' to use for a new error message. // >>> If you do use this number for a new error template, please increment this value. // >>> - GALxxx_NEXT_MESSAGE_NUMBER_TO_USE = 1274 + GALxxx_NEXT_MESSAGE_NUMBER_TO_USE = 1278 ) diff --git a/modules/cli/pkg/launcher/jvmLauncher.go b/modules/cli/pkg/launcher/jvmLauncher.go index 6e25b639b..fa78dd291 100644 --- a/modules/cli/pkg/launcher/jvmLauncher.go +++ b/modules/cli/pkg/launcher/jvmLauncher.go @@ -111,6 +111,9 @@ type RunsSubmitLocalCmdParameters struct { // A string containing the url of the gherkin test file to be exceuted GherkinURL string + + // A list of strings containing a selection of test methods to be run. + TestMethods []string } const ( @@ -257,6 +260,16 @@ func (launcher *JvmLauncher) SubmitTestRun( err = checkGherkinURLisValid(gherkinURL) } + if err == nil { + // Validate test methods if provided + for _, method := range launcher.cmdParams.TestMethods { + err = utils.ValidateJavaMethodName(method) + if err != nil { + break + } + } + } + if err == nil { var jwt = "" @@ -279,7 +292,7 @@ func (launcher *JvmLauncher) SubmitTestRun( launcher.bootstrapProps, launcher.galasaHome, launcher.fileSystem, launcher.javaHome, obrs, - *testClassToLaunch, launcher.cmdParams.RemoteMaven, launcher.cmdParams.LocalMaven, + *testClassToLaunch, launcher.cmdParams.TestMethods, launcher.cmdParams.RemoteMaven, launcher.cmdParams.LocalMaven, launcher.cmdParams.TargetGalasaVersion, overridesFilePath, gherkinURL, isTraceEnabled, @@ -606,6 +619,7 @@ func getCommandSyntax( javaHome string, testObrs []utils.MavenCoordinates, testLocation TestLocation, + testMethods []string, remoteMaven string, localMaven string, galasaVersionToRun string, @@ -655,6 +669,12 @@ func getCommandSyntax( // --test ${TEST_BUNDLE}/${TEST_JAVA_CLASS} args = append(args, "--test") args = append(args, testLocation.OSGiBundleName+"/"+testLocation.QualifiedJavaClassName) + + // --methods ${TEST_METHOD} + for _, method := range testMethods { + args = append(args, "--methods") + args = append(args, method) + } } } diff --git a/modules/cli/pkg/launcher/jvmLauncher_test.go b/modules/cli/pkg/launcher/jvmLauncher_test.go index 8d91276df..93d621c3f 100644 --- a/modules/cli/pkg/launcher/jvmLauncher_test.go +++ b/modules/cli/pkg/launcher/jvmLauncher_test.go @@ -259,6 +259,56 @@ func TestCanLaunchLocalJvmTest(t *testing.T) { } } +func TestLaunchLocalJvmTestWithInvalidMethodNameThrowsError(t *testing.T) { + // Given... + bootstrapProps, env, fs, embeddedReadOnlyFS, + jvmLaunchParams, timeService, timedSleeper, mockProcessFactory, galasaHome := NewMockLauncherParams() + + jvmLaunchParams.TestMethods = []string{"invalid method name!"} + + mockFactory := &utils.MockFactory{ + Env: env, + FileSystem: fs, + TimeService: timeService, + } + + launcher, err := NewJVMLauncher( + mockFactory, + bootstrapProps, embeddedReadOnlyFS, + jvmLaunchParams, mockProcessFactory, galasaHome, timedSleeper, + ) + + if err != nil { + assert.Fail(t, "JVM launcher should have been creatable.") + } + assert.NotNil(t, launcher, "Launcher returned is nil!") + + isTraceEnabled := true + var overrides map[string]interface{} = make(map[string]interface{}) + + var tags []string + + // When... + _, err = launcher.SubmitTestRun( + "myGroup", + "galasa.dev.example.banking.account/galasa.dev.example.banking.account.TestAccount", + "myRequestType-UnitTest", + "myRequestor", + "myRequestor", + "unitTestStream", + "mvn:myGroup/myArtifact/myClassifier/obr", + isTraceEnabled, + "", // No Gherkin URL supplied + "", // No Gherkin Feature supplied + overrides, + tags, + ) + + // Then... + assert.ErrorContains(t, err, "GAL1276E") + assert.ErrorContains(t, err, "Invalid Java method name") +} + func TestCanGetRunGroupStatus(t *testing.T) { // Given... env := utils.NewMockEnv() @@ -519,6 +569,7 @@ func getDefaultCommandSyntaxTestParameters() ( string, string, bool, + []string, ) { bootstrapProps := getBasicBootstrapProperties() fs := files.NewOverridableMockFileSystem() @@ -546,8 +597,10 @@ func getDefaultCommandSyntaxTestParameters() ( env := utils.NewMockEnv() galasaHome, _ := utils.NewGalasaHome(fs, env, "") + testMethods := make([]string, 0) + return bootstrapProps, env, galasaHome, fs, javaHome, testObrs, testLocation, - remoteMaven, localMaven, galasaVersionToRun, overridesFilePath, isTraceEnabled + remoteMaven, localMaven, galasaVersionToRun, overridesFilePath, isTraceEnabled, testMethods } func TestCommandIncludesTraceWhenTraceIsEnabled(t *testing.T) { @@ -560,7 +613,8 @@ func TestCommandIncludesTraceWhenTraceIsEnabled(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := true isDebugEnabled := false @@ -573,6 +627,7 @@ func TestCommandIncludesTraceWhenTraceIsEnabled(t *testing.T) { fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -599,7 +654,8 @@ func TestCommandDoesNotIncludeTraceWhenTraceIsDisabled(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := false @@ -610,6 +666,7 @@ func TestCommandDoesNotIncludeTraceWhenTraceIsDisabled(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -635,7 +692,8 @@ func TestCommandSyntaxContainsJavaHomeUnixSlashes(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - isTraceEnabled := getDefaultCommandSyntaxTestParameters() + isTraceEnabled, + testMethods := getDefaultCommandSyntaxTestParameters() javaHome := "myJavaHome" fs.SetFilePathSeparator("/") @@ -647,6 +705,7 @@ func TestCommandSyntaxContainsJavaHomeUnixSlashes(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -673,7 +732,8 @@ func TestCommandSyntaxContainsJavaHomeWindowsSlashes(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - isTraceEnabled := getDefaultCommandSyntaxTestParameters() + isTraceEnabled, + testMethods := getDefaultCommandSyntaxTestParameters() javaHome := "myJavaHome" fs.SetFilePathSeparator("\\") @@ -687,6 +747,7 @@ func TestCommandSyntaxContainsJavaHomeWindowsSlashes(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -747,7 +808,8 @@ func TestCommandIncludesGALASA_HOMESystemProperty(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := true isDebugEnabled := false @@ -760,6 +822,7 @@ func TestCommandIncludesGALASA_HOMESystemProperty(t *testing.T) { fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -789,7 +852,8 @@ func TestCommandAllDashDSystemPropertiesPassedAppearBeforeTheDashJar(t *testing. localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := true isDebugEnabled := false @@ -802,6 +866,7 @@ func TestCommandAllDashDSystemPropertiesPassedAppearBeforeTheDashJar(t *testing. fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -870,7 +935,8 @@ func TestCommandIncludesFlagsFromBootstrapProperties(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := false @@ -881,6 +947,7 @@ func TestCommandIncludesFlagsFromBootstrapProperties(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -908,7 +975,8 @@ func TestCommandIncludesTwoFlagsFromBootstrapProperties(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() bootstrapProps[api.BOOTSTRAP_PROPERTY_NAME_LOCAL_JVM_LAUNCH_OPTIONS] = "-Xmx40m -Xms20m" isTraceEnabled := false @@ -920,6 +988,7 @@ func TestCommandIncludesTwoFlagsFromBootstrapProperties(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -948,7 +1017,8 @@ func TestCommandIncludesDefaultDebugPortAndMode(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := true // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -959,6 +1029,7 @@ func TestCommandIncludesDefaultDebugPortAndMode(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -986,7 +1057,8 @@ func TestCommandDrawsValidDebugPortFromBootstrap(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := true // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -999,6 +1071,7 @@ func TestCommandDrawsValidDebugPortFromBootstrap(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -1026,7 +1099,8 @@ func TestCommandDrawsInvalidDebugPortFromBootstrap(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := true // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -1039,6 +1113,7 @@ func TestCommandDrawsInvalidDebugPortFromBootstrap(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -1066,7 +1141,8 @@ func TestCommandDrawsValidDebugModeFromBootstrap(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := true // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -1079,6 +1155,7 @@ func TestCommandDrawsValidDebugModeFromBootstrap(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -1112,7 +1189,8 @@ func TestCommandDrawsInvalidDebugModeFromBootstrap(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := true // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -1125,6 +1203,7 @@ func TestCommandDrawsInvalidDebugModeFromBootstrap(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -1152,7 +1231,8 @@ func TestCommandDrawsValidDebugModeListenFromCommandLine(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := true // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -1163,6 +1243,7 @@ func TestCommandDrawsValidDebugModeListenFromCommandLine(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -1196,7 +1277,8 @@ func TestCommandDrawsValidDebugModeAttachFromCommandLine(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := true // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -1207,6 +1289,7 @@ func TestCommandDrawsValidDebugModeAttachFromCommandLine(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -1240,7 +1323,8 @@ func TestCommandDrawsInvalidDebugModeFromCommandLine(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := true // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -1251,6 +1335,7 @@ func TestCommandDrawsInvalidDebugModeFromCommandLine(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -1279,7 +1364,8 @@ func TestLocalMavenNotSetDefaults(t *testing.T) { localMaven, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := false // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -1291,6 +1377,7 @@ func TestLocalMavenNotSetDefaults(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -1319,7 +1406,8 @@ func TestLocalMavenSet(t *testing.T) { _, galasaVersionToRun, overridesFilePath, - _ := getDefaultCommandSyntaxTestParameters() + _, + testMethods := getDefaultCommandSyntaxTestParameters() isTraceEnabled := false isDebugEnabled := false // <<<< Debug is turned on. No overrides to debugPort in either boostrap or explicit command option. @@ -1332,6 +1420,7 @@ func TestLocalMavenSet(t *testing.T) { bootstrapProps, galasaHome, fs, javaHome, testObrs, testLocation, + testMethods, remoteMaven, localMaven, galasaVersionToRun, @@ -1349,6 +1438,50 @@ func TestLocalMavenSet(t *testing.T) { assert.Contains(t, args, "mavenRepo") } +func TestMethodsAreSetWhenProvided(t *testing.T) { + // For... + bootstrapProps, + _, galasaHome, fs, + javaHome, + testObrs, + testLocation, + remoteMaven, + localMaven, + galasaVersionToRun, + overridesFilePath, + _, + _ := getDefaultCommandSyntaxTestParameters() + + testMethods := []string{"method1", "method2"} + isTraceEnabled := false + isDebugEnabled := false + var debugPort uint32 = 0 + debugMode := "" + + // When... + _, args, err := getCommandSyntax( + bootstrapProps, galasaHome, fs, javaHome, + testObrs, + testLocation, + testMethods, + remoteMaven, + localMaven, + galasaVersionToRun, + overridesFilePath, + "", // No Gherkin URL supplied + isTraceEnabled, + isDebugEnabled, debugPort, debugMode, + BLANK_JWT, + ) + + // Then... + assert.Nil(t, err) + + assert.Contains(t, args, "--methods") + assert.Contains(t, args, "method1") + assert.Contains(t, args, "method2") +} + func NewMockGherkinParams() ( props.JavaProperties, *utils.MockEnv, diff --git a/modules/cli/pkg/runs/junitReporter.go b/modules/cli/pkg/runs/junitReporter.go index 43a975286..a4e0504c2 100644 --- a/modules/cli/pkg/runs/junitReporter.go +++ b/modules/cli/pkg/runs/junitReporter.go @@ -80,7 +80,7 @@ func ReportJunit( testSuites.Tests = testSuites.Tests + 1 testSuite.Tests = testSuite.Tests + 1 - if !strings.HasPrefix(method.Result, "Passed") { + if !strings.HasPrefix(method.Result, "Passed") && !strings.HasPrefix(method.Result, "Ignored") { testSuites.Failures = testSuites.Failures + 1 testSuite.Failures = testSuite.Failures + 1 diff --git a/modules/cli/pkg/utils/javaMethodName.go b/modules/cli/pkg/utils/javaMethodName.go new file mode 100644 index 000000000..a30190005 --- /dev/null +++ b/modules/cli/pkg/utils/javaMethodName.go @@ -0,0 +1,53 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package utils + +import ( + "unicode" + + galasaErrors "github.com/galasa-dev/cli/pkg/errors" +) + +// ValidateJavaMethodName validates that a string is a valid Java method name. +// Java method names must: +// - Start with a letter (a-z, A-Z) +// - Contain only letters, digits, underscores, or dollar signs +// - Not be a Java reserved keyword +// - Not be empty +func ValidateJavaMethodName(methodName string) error { + var err error + + if methodName == "" { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_METHOD_NAME_BLANK) + } + + if err == nil { + // Check first character - must be a letter only (not underscore or dollar sign) + firstChar := rune(methodName[0]) + if !unicode.IsLetter(firstChar) { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_FIRST_CHAR_IN_METHOD_NAME, methodName, string(firstChar)) + } + } + + if err == nil { + // Check remaining characters - can include letters, digits, underscore, dollar sign + for _, c := range methodName { + if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '$' { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_CHAR_IN_METHOD_NAME, methodName, string(c)) + break + } + } + } + + if err == nil { + // Check if the method name is a reserved Java keyword + if isJavaReservedWord(methodName) { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_METHOD_RESERVED_WORD, methodName) + } + } + + return err +} diff --git a/modules/cli/pkg/utils/javaMethodName_test.go b/modules/cli/pkg/utils/javaMethodName_test.go new file mode 100644 index 000000000..6f3372c52 --- /dev/null +++ b/modules/cli/pkg/utils/javaMethodName_test.go @@ -0,0 +1,169 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// Test valid Java method names +func TestValidateJavaMethodNameWellFormed(t *testing.T) { + err := ValidateJavaMethodName("testMethod") + assert.Nil(t, err) +} + +func TestValidateJavaMethodNameWithDigits(t *testing.T) { + err := ValidateJavaMethodName("testMethod123") + assert.Nil(t, err) +} + +func TestValidateJavaMethodNameWithUnderscore(t *testing.T) { + err := ValidateJavaMethodName("test_method") + assert.Nil(t, err) +} + +func TestValidateJavaMethodNameWithDollarSign(t *testing.T) { + err := ValidateJavaMethodName("test$method") + assert.Nil(t, err) +} + +func TestValidateJavaMethodNameCamelCase(t *testing.T) { + err := ValidateJavaMethodName("testMethodName") + assert.Nil(t, err) +} + +func TestValidateJavaMethodNameSingleLetter(t *testing.T) { + err := ValidateJavaMethodName("a") + assert.Nil(t, err) +} + +func TestValidateJavaMethodNameUpperCase(t *testing.T) { + err := ValidateJavaMethodName("TestMethod") + assert.Nil(t, err) +} + +// Test invalid Java method names - blank +func TestValidateJavaMethodNameBlank(t *testing.T) { + err := ValidateJavaMethodName("") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1274E") + assert.Contains(t, err.Error(), "should not be blank") +} + +// Test invalid Java method names - bad first character +func TestValidateJavaMethodNameStartsWithDigit(t *testing.T) { + err := ValidateJavaMethodName("1testMethod") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1275E") + assert.Contains(t, err.Error(), "should start with a letter") +} + +func TestValidateJavaMethodNameStartsWithUnderscore(t *testing.T) { + err := ValidateJavaMethodName("_testMethod") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1275E") + assert.Contains(t, err.Error(), "should start with a letter") +} + +func TestValidateJavaMethodNameStartsWithDollarSign(t *testing.T) { + err := ValidateJavaMethodName("$testMethod") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1275E") + assert.Contains(t, err.Error(), "should start with a letter") +} + +// Test invalid Java method names - bad characters +func TestValidateJavaMethodNameWithSpace(t *testing.T) { + err := ValidateJavaMethodName("test method") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1276E") + assert.Contains(t, err.Error(), "should not contain") +} + +func TestValidateJavaMethodNameWithDash(t *testing.T) { + err := ValidateJavaMethodName("test-method") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1276E") + assert.Contains(t, err.Error(), "should not contain") +} + +func TestValidateJavaMethodNameWithDot(t *testing.T) { + err := ValidateJavaMethodName("test.method") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1276E") + assert.Contains(t, err.Error(), "should not contain") +} + +func TestValidateJavaMethodNameWithSpecialChars(t *testing.T) { + err := ValidateJavaMethodName("test@method") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1276E") + assert.Contains(t, err.Error(), "should not contain") +} + +func TestValidateJavaMethodNameWithParentheses(t *testing.T) { + err := ValidateJavaMethodName("testMethod()") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1276E") + assert.Contains(t, err.Error(), "should not contain") +} + +// Test invalid Java method names - reserved keywords +func TestValidateJavaMethodNameReservedWordClass(t *testing.T) { + err := ValidateJavaMethodName("class") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1277E") + assert.Contains(t, err.Error(), "reserved Java keyword") +} + +func TestValidateJavaMethodNameReservedWordPublic(t *testing.T) { + err := ValidateJavaMethodName("public") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1277E") + assert.Contains(t, err.Error(), "reserved Java keyword") +} + +func TestValidateJavaMethodNameReservedWordStatic(t *testing.T) { + err := ValidateJavaMethodName("static") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1277E") + assert.Contains(t, err.Error(), "reserved Java keyword") +} + +func TestValidateJavaMethodNameReservedWordVoid(t *testing.T) { + err := ValidateJavaMethodName("void") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1277E") + assert.Contains(t, err.Error(), "reserved Java keyword") +} + +func TestValidateJavaMethodNameReservedWordReturn(t *testing.T) { + err := ValidateJavaMethodName("return") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1277E") + assert.Contains(t, err.Error(), "reserved Java keyword") +} + +func TestValidateJavaMethodNameReservedWordIf(t *testing.T) { + err := ValidateJavaMethodName("if") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "GAL1277E") + assert.Contains(t, err.Error(), "reserved Java keyword") +} + +// Test edge cases - reserved words as part of method name (should be valid) +func TestValidateJavaMethodNameContainsReservedWord(t *testing.T) { + err := ValidateJavaMethodName("testClassMethod") + assert.Nil(t, err, "Method name containing 'class' as substring should be valid") +} + +func TestValidateJavaMethodNameEndsWithReservedWord(t *testing.T) { + err := ValidateJavaMethodName("getClass") + assert.Nil(t, err, "Method name ending with 'class' should be valid") +} + diff --git a/modules/cli/test-galasactl-local.sh b/modules/cli/test-galasactl-local.sh index f3e584cc6..085899018 100755 --- a/modules/cli/test-galasactl-local.sh +++ b/modules/cli/test-galasactl-local.sh @@ -247,6 +247,48 @@ function submit_local_test { success "Test ran OK" } +# Run a given test method using the galasactl locally in a JVM +function run_local_test_method { + + h2 "Running a specific test method locally using galasactl" + + cd ${BASEDIR}/temp/*banking + + BUNDLE=$1 + JAVA_CLASS=$2 + TEST_METHOD_TO_RUN=$3 + OBR_GROUP_ID=$4 + OBR_ARTIFACT_ID=$5 + OBR_VERSION=$6 + + REMOTE_MAVEN=https://development.galasa.dev/main/maven-repo/obr/ + + GALASACTL="${BASEDIR}/bin/${binary}" + + ${GALASACTL} runs submit local \ + --obr mvn:${OBR_GROUP_ID}/${OBR_ARTIFACT_ID}/${OBR_VERSION}/obr \ + --remoteMaven ${REMOTE_MAVEN} \ + --class ${BUNDLE}/${JAVA_CLASS} \ + --methods ${TEST_METHOD_TO_RUN} \ + --throttle 1 \ + --requesttype automated-test \ + --poll 10 \ + --progress 1 \ + --log - \ + --reportjunit testMethodRunJunitReport.xml \ + --reportjson testMethodRunJsonReport.json + + # Uncomment this if testing that a test that should fail, fails + # --noexitcodeontestfailures \ + + rc=$? + if [[ "${rc}" != "0" ]]; then + error "Failed to run the test" + exit 1 + fi + success "Test ran OK" +} + function run_test_locally_using_galasactl { # Run the Payee tests. @@ -262,8 +304,30 @@ function run_test_locally_using_galasactl { export testResult="Passed" submit_local_test $TEST_BUNDLE $TEST_JAVA_CLASS $TEST_OBR_GROUP_ID $TEST_OBR_ARTIFACT_ID $TEST_OBR_VERSION - check_junit_report $totalTests $failedTests - check_json_report $testName $testResult + check_junit_report $totalTests $failedTests "junitReport.xml" + check_json_report $testName $testResult "jsonReport.json" +} + +function run_selected_test_method_locally_using_galasactl { + + # Run a given sample test method. + TEST_BUNDLE=dev.galasa.example.banking.payee + TEST_JAVA_CLASS=dev.galasa.example.banking.payee.TestPayeeExtended + TEST_OBR_GROUP_ID=dev.galasa.example.banking + TEST_OBR_ARTIFACT_ID=dev.galasa.example.banking.obr + TEST_OBR_VERSION=0.0.1-SNAPSHOT + TEST_METHOD_NAME="simpleSampleTest" + + totalTests=3 + failedTests=0 + testResult="Passed" + + run_local_test_method $TEST_BUNDLE $TEST_JAVA_CLASS $TEST_METHOD_NAME $TEST_OBR_GROUP_ID $TEST_OBR_ARTIFACT_ID $TEST_OBR_VERSION + check_junit_report $totalTests $failedTests "testMethodRunJunitReport.xml" + check_json_report $TEST_METHOD_NAME $testResult "testMethodRunJsonReport.json" + + # Verify that all test methods except simpleSampleTest are marked as Ignored + check_ignored_tests_in_json_report $TEST_METHOD_NAME "testMethodRunJsonReport.json" } function check_junit_report { @@ -272,8 +336,8 @@ function check_junit_report { totalTests=$1 failedTests=$2 + junitReportFile=$3 - junitReportFile="junitReport.xml" stringToFind="name=\"Galasa test run\" tests=\"${totalTests}\" failures=\"${failedTests}\"" echo "string to find: "${stringToFind} @@ -291,8 +355,8 @@ function check_json_report { testName=$1 testResult=$2 - - jsonReportFile="jsonReport.json" + jsonReportFile=$3 + stringToFind=" \"tests\": \\[ { \"name\": \"${testName}\", @@ -309,6 +373,42 @@ function check_json_report { success "Json report was created successfully" } +function check_ignored_tests_in_json_report { + + cd ${BASEDIR}/temp/*banking + + runTestMethodName=$1 + jsonReportFile=$2 + + h2 "Checking that all test methods except '${runTestMethodName}' are marked as Ignored in JSON report" + + # Check that the JSON report contains ignored tests + # We need to verify that all test methods except the one we ran have result "Ignored" + + # Count total tests in the JSON report + totalTests=$(grep -A 2 '"name":' ${jsonReportFile} | grep -c '"result":') + info "Total test methods found in JSON report: ${totalTests}" + + if [[ "${totalTests}" -lt "2" ]]; then + error "Expected at least 2 test methods in the JSON report (1 run + at least 1 ignored) - see $(pwd)/${jsonReportFile}" + exit 1 + fi + + # Count ignored tests + ignoredTests=$(grep -c '"result": "Ignored"' ${jsonReportFile} || echo "0") + info "Ignored test methods found in JSON report: ${ignoredTests}" + + # Expected ignored tests = total tests - 1 (the one we ran) + expectedIgnoredTests=$(($totalTests - 1)) + + if [[ "${ignoredTests}" != "${expectedIgnoredTests}" ]]; then + error "Expected ${expectedIgnoredTests} ignored tests but found ${ignoredTests} - see $(pwd)/${jsonReportFile}" + exit 1 + fi + + success "The correct number of test methods were marked as Ignored in JSON report" +} + function cleanup_local_maven_repo { rm -fr ~/.m2/repository/dev/galasa/example } @@ -325,6 +425,7 @@ cleanup_local_maven_repo build_generated_source run_test_locally_using_galasactl +run_selected_test_method_locally_using_galasactl CALLED_BY_MAIN="true" source ${BASEDIR}/test-scripts/gherkin-runs-tests.sh diff --git a/modules/extensions/galasa-extensions-parent/dev.galasa.ras.couchdb/src/test/java/dev/galasa/ras/couchdb/internal/mocks/MockIRun.java b/modules/extensions/galasa-extensions-parent/dev.galasa.ras.couchdb/src/test/java/dev/galasa/ras/couchdb/internal/mocks/MockIRun.java index 520f39bcd..e34cc3fdb 100644 --- a/modules/extensions/galasa-extensions-parent/dev.galasa.ras.couchdb/src/test/java/dev/galasa/ras/couchdb/internal/mocks/MockIRun.java +++ b/modules/extensions/galasa-extensions-parent/dev.galasa.ras.couchdb/src/test/java/dev/galasa/ras/couchdb/internal/mocks/MockIRun.java @@ -175,4 +175,9 @@ public Instant getInterruptedAt() { public Instant getAllocatedTimeout() { throw new UnsupportedOperationException("Unimplemented method 'getAllocatedTimeout'"); } + + @Override + public List getRequestedTestMethods() { + throw new UnsupportedOperationException("Unimplemented method 'getRequestedTestMethods'"); + } } diff --git a/modules/framework/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/mocks/MockIFrameworkRuns.java b/modules/framework/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/mocks/MockIFrameworkRuns.java index e323edd8d..0c18c9508 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/mocks/MockIFrameworkRuns.java +++ b/modules/framework/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/mocks/MockIFrameworkRuns.java @@ -68,11 +68,12 @@ public MockIFrameworkRuns(List runs) { @Override public @NotNull IRun submitRun(String type, String requestor, String user, String bundleName, String testName, String groupName, String mavenRepository, String obr, String stream, boolean local, boolean trace, Set tags, Properties overrides, - SharedEnvironmentPhase sharedEnvironmentPhase, String sharedEnvironmentRunName, String language, String submissionId) - throws FrameworkException { - if (stream.equals("null")){ - throw new FrameworkException(language); - } + SharedEnvironmentPhase sharedEnvironmentPhase, String sharedEnvironmentRunName, String language, String submissionId, + List requestedTestMethods + ) throws FrameworkException { + if (stream.equals("null")){ + throw new FrameworkException(language); + } return new MockIRun("runname", type, requestor, user, bundleName + "/" + testName, null, bundleName, testName, groupName, this.mockSubmissionId, tags); } diff --git a/modules/framework/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/mocks/MockIRun.java b/modules/framework/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/mocks/MockIRun.java index 50f995ef9..6801f07d7 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/mocks/MockIRun.java +++ b/modules/framework/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/mocks/MockIRun.java @@ -258,4 +258,9 @@ public Instant getInterruptedAt() { public Instant getAllocatedTimeout() { throw new UnsupportedOperationException("Unimplemented method 'getAllocatedTimeout'"); } + + @Override + public List getRequestedTestMethods() { + throw new UnsupportedOperationException("Unimplemented method 'getRequestedTestMethods'"); + } } diff --git a/modules/framework/galasa-parent/dev.galasa.framework.api.runs/src/main/java/dev/galasa/framework/api/runs/common/GroupRuns.java b/modules/framework/galasa-parent/dev.galasa.framework.api.runs/src/main/java/dev/galasa/framework/api/runs/common/GroupRuns.java index 146107308..ee63a080e 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework.api.runs/src/main/java/dev/galasa/framework/api/runs/common/GroupRuns.java +++ b/modules/framework/galasa-parent/dev.galasa.framework.api.runs/src/main/java/dev/galasa/framework/api/runs/common/GroupRuns.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -27,7 +28,6 @@ import dev.galasa.framework.api.common.InternalServletException; import dev.galasa.framework.api.common.ProtectedRoute; import dev.galasa.framework.api.common.ResponseBuilder; -import dev.galasa.framework.api.common.SystemEnvironment; import dev.galasa.framework.api.common.ServletError; import dev.galasa.framework.spi.FrameworkException; import dev.galasa.framework.spi.IRun; @@ -45,14 +45,12 @@ public class GroupRuns extends ProtectedRoute { private IResultArchiveStore rasStore; private final GalasaGson gson = new GalasaGson(); private final ITimeService timeService; - private final Environment env; public GroupRuns(ResponseBuilder responseBuilder, String path, IFramework framework, ITimeService timeService) throws RBACException { super(responseBuilder, path, framework.getRBACService()); this.framework = framework; this.rasStore = framework.getResultArchiveStore(); this.timeService = timeService; - this.env = new SystemEnvironment(); } protected List getRuns(String groupName) throws InternalServletException { @@ -148,6 +146,7 @@ public ScheduleStatus scheduleRun(ScheduleRequest request, String groupName, Str try { String submissionId = UUID.randomUUID().toString(); + List requestedTestMethods = new ArrayList<>(); IRun newRun = framework.getFrameworkRuns().submitRun( request.getRequestorType(), @@ -166,7 +165,9 @@ public ScheduleStatus scheduleRun(ScheduleRequest request, String groupName, Str senvPhase, request.getSharedEnvironmentRunName(), "java", - submissionId); + submissionId, + requestedTestMethods + ); Run serializedRun = setwebUiAndRestApiUrls(newRun, env); diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/FrameworkRuns.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/FrameworkRuns.java index 1f15c315b..da0aedb25 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/FrameworkRuns.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/FrameworkRuns.java @@ -190,7 +190,7 @@ public List getAllGroupedRuns(@NotNull String groupName) throws FrameworkE public @NotNull IRun submitRun(String runType, String requestor, String user, String bundleName, @NotNull String testName, String groupName, String mavenRepository, String obr, String stream, boolean local, boolean trace, Set tags, Properties overrides, SharedEnvironmentPhase sharedEnvironmentPhase, String sharedEnvironmentRunName, - String language, String submissionId) throws FrameworkException { + String language, String submissionId, List requestedTestMethods) throws FrameworkException { SubmitRunRequest runRequest = new SubmitRunRequest( runType, requestor, @@ -208,7 +208,8 @@ public List getAllGroupedRuns(@NotNull String groupName) throws FrameworkE overrides, sharedEnvironmentPhase, sharedEnvironmentRunName, - language + language, + requestedTestMethods ); return submitRun(runRequest); } @@ -232,6 +233,7 @@ private boolean storeRun(String runName, SubmitRunRequest runRequest) throws Dyn boolean trace = runRequest.isTraceEnabled(); Set tags = runRequest.getTags(); String runId = generateRasRunId(runName); + List requestedTestMethods = runRequest.getRequestedTestMethods(); // *** Set up the otherRunProperties that will go with the Run number HashMap otherRunProperties = new HashMap<>(); @@ -268,6 +270,11 @@ private boolean storeRun(String runName, SubmitRunRequest runRequest) throws Dyn otherRunProperties.put(getSuffixedRunDssKey(runName, DssPropertyKeyRunNameSuffix.TAGS) ,tagsAsString); } + if (requestedTestMethods != null) { + String requestedTestMethodsAsString = gson.toJson(requestedTestMethods); + otherRunProperties.put(getSuffixedRunDssKey(runName, DssPropertyKeyRunNameSuffix.TEST_METHODS), requestedTestMethodsAsString); + } + otherRunProperties.put(getSuffixedRunDssKey(runName, DssPropertyKeyRunNameSuffix.REQUESTOR), requestor); otherRunProperties.put(getSuffixedRunDssKey(runName, DssPropertyKeyRunNameSuffix.USER), user); diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/RunImpl.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/RunImpl.java index 61b284626..4858a80fe 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/RunImpl.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/RunImpl.java @@ -20,6 +20,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import com.google.gson.reflect.TypeToken; + import dev.galasa.api.run.Run; import dev.galasa.framework.spi.AbstractManager; import dev.galasa.framework.spi.DssPropertyKeyRunNameSuffix; @@ -60,6 +62,7 @@ public class RunImpl implements IRun { private final Instant allocatedTimeout; private List rasActions = new ArrayList<>(); private final Set tags; + private final List requestedTestMethods; private static final Log logger = LogFactory.getLog(RunImpl.class); private static final GalasaGson gson = new GalasaGson(); @@ -98,6 +101,7 @@ public RunImpl(String name, IDynamicStatusStoreService dss) throws DynamicStatus tags = getTagsFromDss(runProperties, prefix); interruptedAt = getInterruptedAtTimeFromDss(runProperties, prefix); allocatedTimeout = getAllocatedTimeoutFromDss(runProperties, prefix); + requestedTestMethods = getRequestedTestMethodsFromDss(runProperties, prefix); String encodedRasActions = runProperties.get(prefix + DssPropertyKeyRunNameSuffix.RAS_ACTIONS); if (encodedRasActions != null) { @@ -186,6 +190,23 @@ private Set getTagsFromDss(Map runProperties, String pre return tags; } + private List getRequestedTestMethodsFromDss(Map runProperties, String prefix) { + List requestedTestMethods = new ArrayList<>(); + String requestedTestMethodsAsString = runProperties.get(prefix + DssPropertyKeyRunNameSuffix.TEST_METHODS); + + if (requestedTestMethodsAsString != null && !requestedTestMethodsAsString.isBlank()) { + try { + TypeToken> listTypeToken = new TypeToken>(){}; + List testMethodsFromJson = gson.fromJson(requestedTestMethodsAsString, listTypeToken.getType()); + requestedTestMethods.addAll(testMethodsFromJson); + } catch (Exception e) { + // Don't fail the test run, fall back to running all tests + logger.error("Failed to de-serialise test methods to run from dss. ", e); + } + } + return requestedTestMethods; + } + private List getRasActionsFromEncodedString(String encodedRasActions) { byte[] rasActionsJsonBytes = Base64.getDecoder().decode(encodedRasActions); String rasActionsJsonStr = new String(rasActionsJsonBytes, StandardCharsets.UTF_8); @@ -337,6 +358,12 @@ public Instant getAllocatedTimeout() { public List getRasActions() { return this.rasActions; } + + @Override + public List getRequestedTestMethods() { + return this.requestedTestMethods; + } + public String toString() { ByteArrayOutputStream buffArray = new ByteArrayOutputStream(); PrintWriter buff = new PrintWriter(buffArray); diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestClassWrapper.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestClassWrapper.java index 37857364f..10fe417b9 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestClassWrapper.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestClassWrapper.java @@ -223,10 +223,19 @@ protected void runAfterClassMethods( @NotNull ITestRunManagers managers ) throws * @param managers * @param dss * @param runName + * @param testMethodNamesToRun * * @throws TestRunException */ - public void runMethods(@NotNull ITestRunManagers managers, IDynamicStatusStoreService dss, String runName) throws TestRunException { + public void runMethods( + @NotNull ITestRunManagers managers, + IDynamicStatusStoreService dss, + String runName, + List testMethodNamesToRun + ) throws TestRunException { + + // Get the filtered list of test methods to run + List testMethodsToRun = getTestMethodsToRun(testMethodNamesToRun); logger.info(LOG_STARTING + LOG_START_LINE + LOG_ASTERS + LOG_START_LINE + "*** Start of test class " + testClass.getName() + LOG_START_LINE + LOG_ASTERS); @@ -253,7 +262,7 @@ public void runMethods(@NotNull ITestRunManagers managers, IDynamicStatusStoreSe TestRunException originalEx = null ; try { try { - runAllMethods(managers, dss, runName); + runAllMethods(managers, dss, runName, testMethodsToRun); } catch( TestRunException ex) { originalEx = ex ; } @@ -292,6 +301,27 @@ public void runMethods(@NotNull ITestRunManagers managers, IDynamicStatusStoreSe } + private List getTestMethodsToRun(List testMethodNames) { + List testMethodsToRun = new ArrayList<>(); + + if (testMethodNames == null || testMethodNames.isEmpty()) { + testMethodsToRun = this.testMethods; + } else { + // Filter test methods to only include those with matching names + // This must be case-sensitive as Java method names are case-sensitive + for (TestMethodWrapper testMethod : this.testMethods) { + if (testMethodNames.contains(testMethod.getName())) { + testMethodsToRun.add(testMethod); + } + } + } + + if (testMethodsToRun.isEmpty()) { + logger.warn("No test methods matching the specified method names were found"); + } + return testMethodsToRun; + } + private void logEndTestLogLine() { logger.info(LOG_ENDING + LOG_START_LINE + LOG_ASTERS + LOG_START_LINE + "*** " + getResult().getName() + " - Test class " + testClass.getName() + LOG_START_LINE + LOG_ASTERS); @@ -306,7 +336,7 @@ private void updateTestStructureWithResult() { this.testStructure.setResult(getResult().getName()); } - private void runAllMethods(ITestRunManagers managers, IDynamicStatusStoreService dss, String runName ) throws TestRunException { + private void runAllMethods(ITestRunManagers managers, IDynamicStatusStoreService dss, String runName, List testMethodsToRun) throws TestRunException { runBeforeClassMethods(managers); @@ -319,7 +349,7 @@ private void runAllMethods(ITestRunManagers managers, IDynamicStatusStoreService // the result is not a full stop (i.e. a failed or env failed result). if (getResult() == null || !getResult().isFullStop()) { // Run @Test methods - runTestMethods(managers, dss, runName); + runTestMethods(managers, dss, runName, testMethodsToRun); } } catch( TestRunException ex ) { // Save the original exception, so it can be re-thrown later. @@ -370,12 +400,13 @@ protected void runGenericMethods(@NotNull ITestRunManagers managers, ArrayList testMethodsToRun) throws TestRunException { try { dss.put("run." + runName + "." + DssPropertyKeyRunNameSuffix.METHOD_TOTAL, Integer.toString(this.testMethods.size())); Set testMethodResultNamesSoFar = new HashSet<>(); int actualMethod = 0; + for (TestMethodWrapper testMethod : this.testMethods) { // Check to see if the run has been cancelled... @@ -384,17 +415,24 @@ protected void runTestMethods(@NotNull ITestRunManagers managers, IDynamicStatus break; } - actualMethod++; - dss.put("run." + runName + "." + DssPropertyKeyRunNameSuffix.METHOD_CURRENT, Integer.toString(actualMethod)); - dss.put("run." + runName + "." + DssPropertyKeyRunNameSuffix.METHOD_NAME, testMethod.getName()); - // Run @Test method - testMethod.invoke(managers, this.testClassObject, this.continueOnTestFailure, this); - // Setting the result so far after every @Test - // method happens inside the testMethod class. - testMethodResultNamesSoFar.add(testMethod.getResult().getName()); - if (testMethod.fullStop()) { - break; + // Check to see if the test method is in the list of test methods to run + if (testMethodsToRun.contains(testMethod)) { + actualMethod++; + dss.put("run." + runName + "." + DssPropertyKeyRunNameSuffix.METHOD_CURRENT, Integer.toString(actualMethod)); + dss.put("run." + runName + "." + DssPropertyKeyRunNameSuffix.METHOD_NAME, testMethod.getName()); + // Run @Test method + testMethod.invoke(managers, this.testClassObject, this.continueOnTestFailure, this); + // Setting the result so far after every @Test + // method happens inside the testMethod class. + testMethodResultNamesSoFar.add(testMethod.getResult().getName()); + if (testMethod.fullStop()) { + break; + } + } else { + // The test method is not in the list of test methods to run, so mark it as ignored + testMethod.markTestAndLinkedMethodsIgnored(Result.ignore("This method was not requested to be run")); } + } // If all of the test methods were ignored, then 'ignored' will be the only result recorded, diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestMethodWrapper.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestMethodWrapper.java index 5dfbcf1cc..38c4a861c 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestMethodWrapper.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestMethodWrapper.java @@ -74,7 +74,7 @@ public void invoke( + TestClassWrapper.LOG_ASTERS); logger.info("Ignoring " + executionMethod.getName() + " due to " + ignoredResult.getReason()); - markTestAndLinkedMethodsIgnored(ignoredResult, testClassWrapper, managers); + markTestAndLinkedMethodsIgnored(ignoredResult); } else { runBeforeMethods(managers, testClassObject, testClassWrapper); @@ -192,7 +192,7 @@ public String getName() { return this.testMethod.getName(); } - private void markTestAndLinkedMethodsIgnored(Result ignoredResult, TestClassWrapper testClassWrapper, ITestRunManagers managers) { + void markTestAndLinkedMethodsIgnored(Result ignoredResult) { // Mark test method as ignored testMethod.setResult(ignoredResult); this.result = ignoredResult; diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestRunner.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestRunner.java index 51dd6d815..e15473388 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestRunner.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/TestRunner.java @@ -498,7 +498,7 @@ private void runTestClassWrapper(TestClassWrapper testClassWrapper, ITestRunMana updateStatus(TestRunLifecycleStatus.RUNNING, null); try { logger.info("Running the test class"); - testClassWrapper.runMethods(managers, dss, runName); + testClassWrapper.runMethods(managers, dss, runName, run.getRequestedTestMethods()); } finally { updateStatus(TestRunLifecycleStatus.RUNDONE, null); } diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/ValidateEcosystem.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/ValidateEcosystem.java index bdd368998..629a326ca 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/ValidateEcosystem.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/ValidateEcosystem.java @@ -6,6 +6,7 @@ package dev.galasa.framework; import java.time.Instant; +import java.util.List; import java.util.Properties; import java.util.Set; import java.util.UUID; @@ -33,7 +34,8 @@ public class ValidateEcosystem { private Environment env; - private static final Set NULL_TAGS = null ; + private static final Set NULL_TAGS = null; + private static final List NULL_METHODS = null; public ValidateEcosystem() { this(new SystemEnvironment()); @@ -143,7 +145,8 @@ private IRun submitCoreManagerTest(IFrameworkRuns frameworkRuns) throws Framewor null, null, null, - submissionId); + submissionId, + NULL_METHODS); logger.info("Test CoreManagerIVT submitted as run " + testRun.getName()); diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/beans/SubmitRunRequest.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/beans/SubmitRunRequest.java index 78616bc16..7dbcc20cd 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/beans/SubmitRunRequest.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/beans/SubmitRunRequest.java @@ -5,7 +5,9 @@ */ package dev.galasa.framework.beans; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Properties; import java.util.Set; @@ -36,6 +38,7 @@ public class SubmitRunRequest { private String language = "java"; private String bundleTest; private String gherkinTest; + private List requestedTestMethods = new ArrayList<>(); public SubmitRunRequest( String runType, @@ -54,7 +57,8 @@ public SubmitRunRequest( Properties overrides, SharedEnvironmentPhase sharedEnvironmentPhase, String sharedEnvironmentRunName, - String language + String language, + List requestedTestMethods ) throws FrameworkException { setTestName(testName); @@ -72,6 +76,11 @@ public SubmitRunRequest( if (tags!=null) { this.tags.addAll(tags); } + + if (requestedTestMethods != null) { + this.requestedTestMethods.addAll(requestedTestMethods); + } + this.overrides = overrides; this.sharedEnvironmentPhase = sharedEnvironmentPhase; this.sharedEnvironmentRunName = sharedEnvironmentRunName; @@ -232,4 +241,8 @@ public void setTags(Set newTags) { public Set getTags() { return this.tags ; } + + public List getRequestedTestMethods() { + return this.requestedTestMethods; + } } diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/internal/init/TestRunInitStrategy.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/internal/init/TestRunInitStrategy.java index 49b8ac3dd..8fc53d44c 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/internal/init/TestRunInitStrategy.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/internal/init/TestRunInitStrategy.java @@ -5,6 +5,8 @@ */ package dev.galasa.framework.internal.init; +import java.util.ArrayList; +import java.util.List; import java.util.Properties; import java.util.Set; import java.util.UUID; @@ -16,6 +18,7 @@ import dev.galasa.framework.IFrameworkInitialisationStrategy; import dev.galasa.framework.beans.Property; import dev.galasa.framework.spi.AbstractManager; +import dev.galasa.framework.spi.ConfigurationPropertyStoreException; import dev.galasa.framework.spi.DssPropertyKeyRunNameSuffix; import dev.galasa.framework.spi.DynamicStatusStoreException; import dev.galasa.framework.spi.FrameworkException; @@ -56,17 +59,36 @@ private String locateRunName(IFramework framework ,IConfigurationPropertyStoreSe if (runName == null) { String testName = AbstractManager.nulled(cps.getProperty("run", "testbundleclass")); String testLanguage = "java"; + List requestedTestMethods = getRequestedTestMethods(cps); + if (testName == null) { testName = AbstractManager.nulled(cps.getProperty("run", "gherkintest")); testLanguage = "gherkin"; } logger.info("Submitting test "+testName); - runName = submitRun(framework, testName, testLanguage); + runName = submitRun(framework, testName, testLanguage, requestedTestMethods); } logger.info("Run name is "+runName); return runName; } + private List getRequestedTestMethods(IConfigurationPropertyStoreService cps) throws ConfigurationPropertyStoreException { + List requestedTestMethods = new ArrayList<>(); + String commaSeparatedRequestedTestMethods = AbstractManager.nulled(cps.getProperty("run", "testmethods")); + + if (commaSeparatedRequestedTestMethods != null && !commaSeparatedRequestedTestMethods.isBlank()) { + String[] testMethods = commaSeparatedRequestedTestMethods.split(","); + + for (String testMethod : testMethods) { + String trimmedMethod = testMethod.trim(); + if (!trimmedMethod.isEmpty()) { + requestedTestMethods.add(trimmedMethod); + } + } + } + return requestedTestMethods; + } + /** * Submit the run and return the run name. * @@ -75,7 +97,7 @@ private String locateRunName(IFramework framework ,IConfigurationPropertyStoreSe * @return The name of the run created. * @throws FrameworkException */ - protected String submitRun(IFramework framework, String runBundleClass, String language) throws FrameworkException { + protected String submitRun(IFramework framework, String runBundleClass, String language, List requestedTestMethods) throws FrameworkException { IRun run = null; IFrameworkRuns frameworkRuns = framework.getFrameworkRuns(); String submissionId = UUID.randomUUID().toString(); @@ -85,10 +107,10 @@ protected String submitRun(IFramework framework, String runBundleClass, String l String split[] = runBundleClass.split("/"); String bundle = split[0]; String test = split[1]; - run = frameworkRuns.submitRun("local", null, null, bundle, test, null, null, null, null, true, false, NULL_TAGS, null, null, null, language, submissionId); + run = frameworkRuns.submitRun("local", null, null, bundle, test, null, null, null, null, true, false, NULL_TAGS, null, null, null, language, submissionId, requestedTestMethods); break; case "gherkin": - run = frameworkRuns.submitRun("local", null, null, null, runBundleClass, null, null, null, null, true, false, NULL_TAGS, null, null, null, language, submissionId); + run = frameworkRuns.submitRun("local", null, null, null, runBundleClass, null, null, null, null, true, false, NULL_TAGS, null, null, null, language, submissionId, null); break; default: throw new FrameworkException("Unknown language to create run"); diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/DssPropertyKeyRunNameSuffix.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/DssPropertyKeyRunNameSuffix.java index be7466cdb..9ba22188f 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/DssPropertyKeyRunNameSuffix.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/DssPropertyKeyRunNameSuffix.java @@ -42,6 +42,7 @@ public enum DssPropertyKeyRunNameSuffix{ TEST("test"), TEST_BUNDLE("testbundle"), TEST_CLASS("testclass"), + TEST_METHODS("testmethods"), TRACE("trace"), WAIT_UNTIL("wait.until"), ; diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/IFrameworkRuns.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/IFrameworkRuns.java index eb975ad20..cd6c85963 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/IFrameworkRuns.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/IFrameworkRuns.java @@ -40,7 +40,8 @@ public enum SharedEnvironmentPhase { @NotNull IRun submitRun(String type, String requestor, String user, String bundleName, String testName, String groupName, String mavenRepository, String obr, String stream, boolean local, boolean trace, Set tags, Properties overrides, - SharedEnvironmentPhase sharedEnvironmentPhase, String sharedEnvironmentRunName, String language, String submissionId) throws FrameworkException; + SharedEnvironmentPhase sharedEnvironmentPhase, String sharedEnvironmentRunName, String language, String submissionId, + List requestedTestMethods) throws FrameworkException; boolean delete(String runname) throws DynamicStatusStoreException; diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/IRun.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/IRun.java index da0a7abf0..e47e8a77f 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/IRun.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/IRun.java @@ -34,6 +34,8 @@ public interface IRun { String getTestClassName(); + List getRequestedTestMethods(); + boolean isLocal(); String getGroup(); diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/utils/GalasaGson.java b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/utils/GalasaGson.java index 4823f6065..bbe592380 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/utils/GalasaGson.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/utils/GalasaGson.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.StringReader; +import java.lang.reflect.Type; import java.io.Reader; import org.apache.commons.io.output.StringBuilderWriter; @@ -47,6 +48,10 @@ public String toJson(Object obj){ public T fromJson(String json, Class classOfT) { return gson.fromJson(json, classOfT); } + + public T fromJson(String json, Type typeOfT) { + return gson.fromJson(json, typeOfT); + } public T fromJson(InputStreamReader inputStreamReader, Class classOfT) throws IOException { return fromJson(readerToString(inputStreamReader), classOfT); diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/FrameworkRunsTest.java b/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/FrameworkRunsTest.java index 22bf52ba0..922ad2227 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/FrameworkRunsTest.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/FrameworkRunsTest.java @@ -82,6 +82,7 @@ public void testSubmitRunReturnsSubmittedRun() throws Exception { boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = List.of("testMethod1", "testMethod2", "testMethod3"); Properties overrides = new Properties(); String override1Key = "override1"; @@ -113,16 +114,19 @@ public void testSubmitRunReturnsSubmittedRun() throws Exception { sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); // Then... assertThat(run).isNotNull(); assertThat(run.getName()).isEqualTo("U1"); assertThat(run.getTest()).isEqualTo(bundleName + "/" + testName); + assertThat(run.getRequestedTestMethods()).containsExactlyElementsOf(requestedTestMethods); // Check that the DSS has been populated with the correct run-related properties assertThat(mockDss.get("request.prefix.U.lastused")).isEqualTo("1"); + assertThat(mockDss.get("run.U1.testmethods")).isEqualTo(gson.toJson(requestedTestMethods)); assertThat(mockDss.get("run.U1."+DssPropertyKeyRunNameSuffix.OBR)).isEqualTo(obr); assertThat(mockDss.get("run.U1."+DssPropertyKeyRunNameSuffix.GROUP)).isEqualTo(groupName); assertThat(mockDss.get("run.U1."+DssPropertyKeyRunNameSuffix.REQUESTOR)).isEqualTo(requestor); @@ -164,6 +168,7 @@ public void testSubmitRunWithMaxRunNumberReachedReturnsSubmittedRunOk() throws E boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); String override1Key = "override1"; @@ -195,7 +200,8 @@ public void testSubmitRunWithMaxRunNumberReachedReturnsSubmittedRunOk() throws E sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); // Then... @@ -248,6 +254,7 @@ public void testSubmitRunWithMaxRunNumberReachedTwiceThrowsError() throws Except boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); String override1Key = "override1"; @@ -280,7 +287,8 @@ public void testSubmitRunWithMaxRunNumberReachedTwiceThrowsError() throws Except sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); }, FrameworkException.class); @@ -312,6 +320,7 @@ public void testSubmitRunWithNoTestNameThrowsCorrectError() throws Exception { boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); @@ -338,7 +347,8 @@ public void testSubmitRunWithNoTestNameThrowsCorrectError() throws Exception { sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); }, FrameworkException.class); @@ -370,6 +380,7 @@ public void testSubmitRunWithNoBundleNameThrowsCorrectError() throws Exception { boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); @@ -396,7 +407,8 @@ public void testSubmitRunWithNoBundleNameThrowsCorrectError() throws Exception { sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); }, FrameworkException.class); @@ -429,6 +441,7 @@ public void testSubmitRunWithNoLanguageDefaultsToJavaBundleFormat() throws Excep boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); @@ -453,7 +466,8 @@ public void testSubmitRunWithNoLanguageDefaultsToJavaBundleFormat() throws Excep sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); // Then... @@ -486,6 +500,7 @@ public void testSubmitRunWithLocalRunTypeSetsRunPrefixCorrectly() throws Excepti boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); @@ -510,7 +525,8 @@ public void testSubmitRunWithLocalRunTypeSetsRunPrefixCorrectly() throws Excepti sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); // Then... @@ -543,6 +559,7 @@ public void testSubmitRunWithGherkinReturnsRunCorrectly() throws Exception { boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); @@ -567,7 +584,8 @@ public void testSubmitRunWithGherkinReturnsRunCorrectly() throws Exception { sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); // Then... @@ -619,6 +637,7 @@ public void testSubmitRunWithSharedEnvironmentBuildPhaseReturnsRunCorrectly() th boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); @@ -640,7 +659,8 @@ public void testSubmitRunWithSharedEnvironmentBuildPhaseReturnsRunCorrectly() th sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); // Then... @@ -693,6 +713,7 @@ public void testSubmitRunWithSharedEnvironmentBuildPhaseAndNoRunNameThrowsError( boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); @@ -715,7 +736,8 @@ public void testSubmitRunWithSharedEnvironmentBuildPhaseAndNoRunNameThrowsError( sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); }, FrameworkException.class); @@ -754,6 +776,7 @@ public void testSubmitRunWithSharedEnvironmentDiscardPhaseReturnsRunCorrectly() boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); @@ -775,7 +798,8 @@ public void testSubmitRunWithSharedEnvironmentDiscardPhaseReturnsRunCorrectly() sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); // Then... @@ -819,6 +843,7 @@ public void testSubmitRunWithDuplicateSharedEnvironmentBuildPhaseRunThrowsError( boolean local = true; boolean trace = true; Set tags = null ; + List requestedTestMethods = null; Properties overrides = new Properties(); @@ -841,7 +866,8 @@ public void testSubmitRunWithDuplicateSharedEnvironmentBuildPhaseRunThrowsError( sharedEnvironmentPhase, sharedEnvironmentRunName, language, - submissionId + submissionId, + requestedTestMethods ); }, FrameworkException.class); diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/TestRunImpl.java b/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/TestRunImpl.java index eee73ff74..3dd9f92e9 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/TestRunImpl.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/TestRunImpl.java @@ -47,6 +47,26 @@ public void testCanCreateARunImplWithARunInDss() throws Exception { assertThat( tagsGotBack).containsExactly("tag1","tag2"); } + @Test + public void testCanCreateARunImplWithRequestedTestMethods() throws Exception { + String name = "U1234"; + Map dssProps = new HashMap(); + + List requestedTestMethods = new ArrayList<>(); + requestedTestMethods.add("method1"); + requestedTestMethods.add("method2"); + + GalasaGson gson = new GalasaGson(); + String testMethodsAsJsonString = gson.toJson(requestedTestMethods); + dssProps.put("run.U1234"+".testmethods", testMethodsAsJsonString); + IDynamicStatusStoreService dss = new MockDSSStore(dssProps); + + RunImpl run = new RunImpl(name,dss); + List testMethodsGotBack = run.getRequestedTestMethods(); + + assertThat(testMethodsGotBack).containsExactly("method1","method2"); + } + @Test public void testCanConvertARunImplToATestStructure() throws Exception { // Given... diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/TestTestClassWrapper.java b/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/TestTestClassWrapper.java index 8b00cc7c4..6d37e7468 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/TestTestClassWrapper.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/TestTestClassWrapper.java @@ -7,6 +7,8 @@ import static org.assertj.core.api.Assertions.*; +import java.util.List; + import javax.validation.constraints.NotNull; import org.apache.commons.logging.Log; @@ -74,14 +76,17 @@ public TestWrapperWhichDoesNothing(String testBundle, } // runTestMethods should throw a fake exception so we can check that other methods get called despite the exception. - protected void runTestMethods(@NotNull ITestRunManagers managers, IDynamicStatusStoreService dss, String runName) throws TestRunException { + @Override + protected void runTestMethods(@NotNull ITestRunManagers managers, IDynamicStatusStoreService dss, String runName, List testMethodsToRun) throws TestRunException { // Do nothing. } + @Override protected void runBeforeClassMethods( @NotNull ITestRunManagers managers ) throws TestRunException { // Do nothing. } + @Override protected void runAfterClassMethods( @NotNull ITestRunManagers managers ) throws TestRunException { // Do nothing. } @@ -100,10 +105,12 @@ public TestClassWrapperWhichThrowsExceptionInRunTestMethod(String testBundle, } // runTestMethods should throw a fake exception so we can check that other methods get called despite the exception. - protected void runTestMethods(@NotNull ITestRunManagers managers, IDynamicStatusStoreService dss, String runName) throws TestRunException { + @Override + protected void runTestMethods(@NotNull ITestRunManagers managers, IDynamicStatusStoreService dss, String runName, List testMethodsToRun) throws TestRunException { throw new TestRunException(fakeExceptionMessage); } + @Override protected void runAfterClassMethods( @NotNull ITestRunManagers managers ) throws TestRunException { isAfterMethodAlreadyCalled = true; } @@ -299,9 +306,10 @@ public void testAfterClassTestMethodsGetCalledIfRunTestMethodThrowsException() t ITestRunManagers managers = new MockTestRunManagers(isContinueOnTestFailureFromCPS, null); String runName = null; + List testMethodsToRun = null; // When... - TestRunException exGotBack = catchThrowableOfType( () -> wrapper.runMethods( managers, dss, runName), TestRunException.class ); + TestRunException exGotBack = catchThrowableOfType( () -> wrapper.runMethods( managers, dss, runName, testMethodsToRun), TestRunException.class ); // Then... @@ -341,9 +349,10 @@ public void testManagersToldAboutTestMethodFailure() throws Exception { MockTestRunManagers managers = new MockTestRunManagers(isContinueOnTestFailureFromCPS, null); String runName = null; + List testMethodsToRun = null; // When... - TestRunException exGotBack = catchThrowableOfType( ()-> wrapper.runMethods( managers, dss, runName), TestRunException.class ); + TestRunException exGotBack = catchThrowableOfType( ()-> wrapper.runMethods( managers, dss, runName, testMethodsToRun), TestRunException.class ); // Then... assertThat(exGotBack).hasMessage(fakeExceptionMessage); @@ -411,12 +420,13 @@ public void testFieldsOfAllBeforeAndAfterMethodsAreSetInStructureOK() throws Exc testBundle, testClass, testStructure, isContinueOnTestFailureFromCPS , ras , interruptedMonitor, (Log)logger); ITestRunManagers managers = new MockTestRunManagers(isContinueOnTestFailureFromCPS, null); + List testMethodsToRun = null; wrapper.parseTestClass(); wrapper.instantiateTestClass(); // When... - wrapper.runMethods(managers, dss, runName); + wrapper.runMethods(managers, dss, runName, testMethodsToRun); // Then... assertThat(testStructure.getMethods().size()).isEqualTo(2); @@ -443,6 +453,59 @@ public void testFieldsOfAllBeforeAndAfterMethodsAreSetInStructureOK() throws Exc } + @Test + public void testRunMethodsCanRunRequestedMethodsAndIgnoresOtherMethods() throws Exception { + + // Given... + String testBundle = null; + String runName = "U1"; + + Class testClass = FakeTestThatCanBeRun.class; + TestStructure testStructure = new TestStructure(); + boolean isContinueOnTestFailureFromCPS = true; + + MockRASStoreService ras = new MockRASStoreService(null); + + + MockIDynamicStatusStoreService dss = new MockIDynamicStatusStoreService(); + + InterruptedMonitorImpl interruptedMonitor = new InterruptedMonitorImpl(dss,runName); + MockLog logger = new MockLog(); + TestClassWrapper wrapper = new TestClassWrapper( + testBundle, testClass, testStructure, isContinueOnTestFailureFromCPS , ras , interruptedMonitor, (Log)logger); + + ITestRunManagers managers = new MockTestRunManagers(isContinueOnTestFailureFromCPS, null); + List testMethodsToRun = List.of("myTestMethod1"); + + wrapper.parseTestClass(); + wrapper.instantiateTestClass(); + + // When... + wrapper.runMethods(managers, dss, runName, testMethodsToRun); + + // Then... + assertThat(testStructure.getMethods().size()).isEqualTo(2); + + // The Befores of the TestMethods should not be pointing to the same object... + assertThat(testStructure.getMethods().get(0).getBefores().get(0)) + .isNotEqualTo(testStructure.getMethods().get(1).getBefores().get(0)); + + // The Afters of the TestMethods should not be pointing to the same object... + assertThat(testStructure.getMethods().get(0).getAfters().get(0)) + .isNotEqualTo(testStructure.getMethods().get(1).getAfters().get(0)); + + // testMethod1 should have passed... + assertThat(testStructure.getMethods().get(0).getBefores().get(0).getResult()).isEqualTo("Passed"); + assertThat(testStructure.getMethods().get(0).getBefores().get(0).getStatus()).isEqualTo("finished"); + assertThat(testStructure.getMethods().get(0).getResult()).isEqualTo("Passed"); + assertThat(testStructure.getMethods().get(0).getAfters().get(0).getResult()).isEqualTo("Passed"); + assertThat(testStructure.getMethods().get(0).getAfters().get(0).getStatus()).isEqualTo("finished"); + + // testMethod2 should have been ignored... + assertThat(testStructure.getMethods().get(1).getBefores().get(0).getResult()).isEqualTo("Ignored"); + assertThat(testStructure.getMethods().get(1).getResult()).isEqualTo("Ignored"); + assertThat(testStructure.getMethods().get(1).getAfters().get(0).getResult()).isEqualTo("Ignored"); + } // If the test is interrupted, it should cancel the test before it does anything with managers. @Test @@ -471,9 +534,10 @@ public void testRunEndsInFinishedCancelledStateIfTestRunGetsInterruptedBeforeItC MockTestRunManagers managers = new MockTestRunManagers(isContinueOnTestFailureFromCPS, null); String runName = null; + List testMethodsToRun = null; // When... - wrapper.runMethods( managers, dss, runName); + wrapper.runMethods( managers, dss, runName, testMethodsToRun); // Then... Result result = wrapper.getResult(); @@ -597,6 +661,7 @@ public void testRunStopsBetweenMethodsIfItsInterrupted() throws Exception { MockTestRunManagers managers = new MockTestRunManagers(isContinueOnTestFailureFromCPS, null); String runName = null; + List testMethodsToRun = null; // Initialize the testClass with our fake date... wrapper.parseTestClass(); @@ -605,7 +670,7 @@ public void testRunStopsBetweenMethodsIfItsInterrupted() throws Exception { testClassInstance.init(dss, testRunName); // When... - wrapper.runMethods( managers, dss, runName); + wrapper.runMethods( managers, dss, runName, testMethodsToRun); // Then... assertThat(testClassInstance.firstMethodCalledCounter).as("Expected the first test method to have been called").isEqualTo(1); @@ -636,12 +701,13 @@ public void testClassWithOnlyIgnoredMethodsSetsTestClassResultToIgnored() throws MockTestRunManagers managers = new MockTestRunManagers(isTestClassToBeIgnored, Result.ignore("IGNORED!")); String runName = null; + List testMethodsToRun = null; wrapper.parseTestClass(); wrapper.instantiateTestClass(); // When... - wrapper.runMethods(managers, dss, runName); + wrapper.runMethods(managers, dss, runName, testMethodsToRun); // Then... Result result = wrapper.getResult(); @@ -669,6 +735,7 @@ public void testClassWithIgnoredMethodSetsCorrectResultWhenAllMethodsPass() thro MockTestRunManagers managers = new MockTestRunManagers(isTestClassToBeIgnored, null); String runName = null; + List testMethodsToRun = null; wrapper.parseTestClass(); wrapper.instantiateTestClass(); @@ -678,7 +745,7 @@ public void testClassWithIgnoredMethodSetsCorrectResultWhenAllMethodsPass() thro testClassInstance.init(managers, isSimulatingTestFailure); // When... - wrapper.runMethods(managers, dss, runName); + wrapper.runMethods(managers, dss, runName, testMethodsToRun); // Then... Result result = wrapper.getResult(); @@ -709,6 +776,7 @@ public void testClassWithIgnoredMethodSetsCorrectResultWhenAMethodFails() throws MockTestRunManagers managers = new MockTestRunManagers(isTestClassToBeIgnored, null); String runName = null; + List testMethodsToRun = null; wrapper.parseTestClass(); wrapper.instantiateTestClass(); @@ -718,7 +786,7 @@ public void testClassWithIgnoredMethodSetsCorrectResultWhenAMethodFails() throws testClassInstance.init(managers, isSimulatingTestFailure); // When... - wrapper.runMethods(managers, dss, runName); + wrapper.runMethods(managers, dss, runName, testMethodsToRun); // Then... Result result = wrapper.getResult(); diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/spi/language/gherkin/MockRun.java b/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/spi/language/gherkin/MockRun.java index 74bfe6daa..1b14b4c95 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/spi/language/gherkin/MockRun.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/spi/language/gherkin/MockRun.java @@ -170,4 +170,9 @@ public Instant getInterruptedAt() { public Instant getAllocatedTimeout() { throw new UnsupportedOperationException("Unimplemented method 'getAllocatedTimeout'"); } + + @Override + public List getRequestedTestMethods() { + throw new UnsupportedOperationException("Unimplemented method 'getRequestedTestMethods'"); + } } diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/testFixtures/java/dev/galasa/framework/mocks/MockFrameworkRuns.java b/modules/framework/galasa-parent/dev.galasa.framework/src/testFixtures/java/dev/galasa/framework/mocks/MockFrameworkRuns.java index 184cfb79e..43ac77483 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/testFixtures/java/dev/galasa/framework/mocks/MockFrameworkRuns.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/testFixtures/java/dev/galasa/framework/mocks/MockFrameworkRuns.java @@ -89,11 +89,12 @@ public IRun getRun(String runname) throws DynamicStatusStoreException { @Override public @NotNull IRun submitRun(String type, String requestor, String user, String bundleName, String testName, String groupName, String mavenRepository, String obr, String stream, boolean local, boolean trace, Set tags,Properties overrides, - SharedEnvironmentPhase sharedEnvironmentPhase, String sharedEnvironmentRunName, String language, String submissionId) - throws FrameworkException { - if (stream.equals("null")){ - throw new FrameworkException(language); - } + SharedEnvironmentPhase sharedEnvironmentPhase, String sharedEnvironmentRunName, String language, String submissionId, + List requestedTestMethods + ) throws FrameworkException { + if (stream.equals("null")){ + throw new FrameworkException(language); + } throw new FrameworkException("Method not implemented in mock class."); } diff --git a/modules/framework/galasa-parent/dev.galasa.framework/src/testFixtures/java/dev/galasa/framework/mocks/MockRun.java b/modules/framework/galasa-parent/dev.galasa.framework/src/testFixtures/java/dev/galasa/framework/mocks/MockRun.java index a6645c77d..d5302fba1 100644 --- a/modules/framework/galasa-parent/dev.galasa.framework/src/testFixtures/java/dev/galasa/framework/mocks/MockRun.java +++ b/modules/framework/galasa-parent/dev.galasa.framework/src/testFixtures/java/dev/galasa/framework/mocks/MockRun.java @@ -40,6 +40,7 @@ public class MockRun implements IRun { private String runId; private List rasActions = new ArrayList<>(); private Set tags = new HashSet(); + private List requestedTestMethods = new ArrayList<>(); public MockRun( String testBundleName, @@ -307,6 +308,15 @@ public void setAllocatedTimeout(Instant allocatedTimeout) { this.allocatedTimeout = allocatedTimeout; } + @Override + public List getRequestedTestMethods() { + return this.requestedTestMethods; + } + + public void setRequestedTestMethods(List requestedTestMethods) { + this.requestedTestMethods = requestedTestMethods; + } + // ------------- un-implemented methods follow ---------------- @Override @@ -335,5 +345,4 @@ public Instant getWaitUntil() { public Run getSerializedRun() { throw new UnsupportedOperationException("Unimplemented method 'getSerializedRun'"); } - } diff --git a/modules/framework/galasa-parent/galasa-boot/src/main/java/dev/galasa/boot/Launcher.java b/modules/framework/galasa-parent/galasa-boot/src/main/java/dev/galasa/boot/Launcher.java index fb52cf68a..2f9d17a48 100644 --- a/modules/framework/galasa-parent/galasa-boot/src/main/java/dev/galasa/boot/Launcher.java +++ b/modules/framework/galasa-parent/galasa-boot/src/main/java/dev/galasa/boot/Launcher.java @@ -57,6 +57,7 @@ public class Launcher { private static final String EXCLUDES_MONITOR_PATTERN_OPTION = "excludes-monitor-pattern"; private static final String OBR_OPTION = "obr"; + private static final String METHODS_OPTION = "methods"; private static final String BOOTSTRAP_OPTION = "bootstrap"; private static final String OVERRIDES_OPTION = "overrides"; private static final String RESOURCEMANAGEMENT_OPTION = "resourcemanagement"; @@ -118,6 +119,7 @@ public class Launcher { private List includeMonitorGlobPatterns = new ArrayList<>(); private List excludeMonitorGlobPatterns = new ArrayList<>(); + private List testMethodNames = new ArrayList<>(); public Environment env; @@ -179,6 +181,13 @@ protected void launch(String[] args) throws LauncherException, InterruptedExcept logger.debug("Test Bundle: " + testBundleName); logger.debug("Test Class: " + testClassName); overridesProperties.setProperty("framework.run.testbundleclass", this.testName); + + // Add methods if specified + if (!testMethodNames.isEmpty()) { + String methodsString = String.join(",", testMethodNames); + logger.debug("Test Methods: " + methodsString); + overridesProperties.setProperty("framework.run.testmethods", methodsString); + } } else if (runName != null) { logger.debug("Test Run: " + runName); overridesProperties.setProperty("framework.run.name", this.runName); @@ -298,6 +307,11 @@ private void processCommandLine(String[] args) throws ParseException { "To use multiple patterns, this flag can be supplied multiple times or by providing a comma-separated list of patterns. "+ "If omitted, no resource monitors will be excluded." ); + options.addOption(null, METHODS_OPTION, true, "Optional. Used alongside " + TEST_OPTION + ". " + + "A list of test method names to run. "+ + "To use multiple methods, this flag can be supplied multiple times or by providing a comma-separated list of method names. "+ + "If omitted, all test methods in the test class will be run." + ); CommandLineParser parser = new DefaultParser(); @@ -338,6 +352,7 @@ private void processCommandLine(String[] args) throws ParseException { checkForLocalMaven(commandLine); checkForRemoteMaven(commandLine); checkForResourceMonitorIncludesAndExcludes(commandLine); + checkForTestMethods(commandLine); isTestRun = commandLine.hasOption(TEST_OPTION) || commandLine.hasOption(RUN_OPTION) || commandLine.hasOption(GHERKIN_OPTION); isResourceManagement = commandLine.hasOption(RESOURCEMANAGEMENT_OPTION); @@ -570,6 +585,22 @@ private void checkForResourceMonitorIncludesAndExcludes(CommandLine commandLine) } } + private void checkForTestMethods(CommandLine commandLine) { + if (commandLine.hasOption(METHODS_OPTION)) { + String[] methodsOptions = commandLine.getOptionValues(METHODS_OPTION); + for (String methodsOption : methodsOptions) { + // Split by comma to support comma-separated values + String[] methods = methodsOption.split(","); + for (String method : methods) { + String trimmedMethod = method.trim(); + if (!trimmedMethod.isEmpty()) { + this.testMethodNames.add(trimmedMethod); + } + } + } + } + } + void setLog4j2PropertiesFile(String log4j2PropertiesFilePath) { if (log4j2PropertiesFilePath != null) { try {