diff --git a/tests/README.md b/tests/README.md index 6d553830..abbdc69d 100644 --- a/tests/README.md +++ b/tests/README.md @@ -25,205 +25,31 @@ The tests which generate devfiles with random content at run time currently cove * Commands: * Exec * Composite + * Apply * Components * Container * Volume + +## Test structure + +* From this repository + - `test/v2/libraryTest/library-test.go`: The go unit test program + - `test/v2/utils/library/*-utils.go` : utilites, used by the test, which contain functions uniqiue to the library tests. Mostly contain the code to modify and check devfile content. +* From the [api respository](https://github.com/devfile/api/tree/master/test/v200/utils/common) + - `test/v200/utils/common/*-utils.go` : utilites, used by the test, which are also used by the api tests. Mostly contain the code to generate valid devfile content. ## Running the tests -1. Go to directory tests/src/tests +1. Go to directory /tests/v2/libraryTest 1. Run ```go test``` or ```go test -v``` -1. The test creates the following files: +1. The test creates the following files: 1. ```./tmp/test.log``` contains log output from the tests. - 1. ```./tmp/parser_v200_verify_test/Test_*.yaml``` are the devfiles which are randomly generated at runtime. The file name matches the name of the test function which resulted in them being created. - 1. ```./tmp/parser_v200_schema_test/*.yaml``` are the pre-created devfiles. - 1. If a test detects an error when compariing properties returned by the parser with expected properties - * ```./tmp/parser_v200_schema_test/Test_*__Parser.yaml``` - property as returned by the parser - * ```./tmp/parser_v200_schema_test/Test_*__Test.yaml``` - property as expected by the test - -Note: each run of the test removes the existing conents of the ```./tmp``` directory - -## Anatomy of parser_v200_verify_test.go test - -Each test in ```parser_v200_verify_test.go``` sets values in a test structure which defines the test to run (additions will new made for new properties as support is added): - - type TestContent struct { - CommandTypes []schema.CommandType - ComponentTypes []schema.ComponentType - FileName string - EditContent bool - } - - -The test then uses one (or both) of two functions to run a test - -* For a single thread test: - * ```func runTest(testContent TestContent, t *testing.T)``` -* For a multi-thread test: - * ```func runMultiThreadTest(testContent TestContent, t *testing.T)``` - -An example test: - - func Test_MultiCommand(t *testing.T) { - testContent := TestContent{} - testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType, schema.CompositeCommandType} - testContent.EditContent = true - testContent.FileName = GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) - } - -Note: ```GetDevFileName()``` is a function which returns the name of a temporary file to create which uses the name of the test file as a subdirectory and the name of test function function as file name. In this example it returns ```./tmp/parser_v200_verify_test/Test_MultiCommand``` - -There are also some constants which control execution of the tests: - - const numThreads = 5 // Number of threads used by multi-thread tests - const maxCommands = 10 // The maximum number of commands to include in a generated devfile - const maxComponents = 10 // The maximum number of components to include in a generated devfile - -## Basic principles of the tests which randomly generates devfiles - -* Each devfile is created in a schema structure. -* Which attributes are set and the values used are randomized. - * For example, the number of commands included in a devfile is randomly generated. - * For example, attribute values are set to randomized strings, numbers or binary. - * For example, a particular optional attribute has a 50% chance of being uncluded in a devfiles. - * Repeated tests give more variety and wider coverage. -* Once the schema structure is complete it is written in one of two ways. - * using the sigs.k8s.io/yaml. - * using the parser. -* Once the devfile is created on disk the parser is used to read and validate it. -* If editing the devfile - * each object is retrieved, modified and written back to the parser - * the parser is used to write the devfile to disk - * the parser is then used to read and validate the modified devfile. -* Each array of objects in then devfile are then retrieved from the parser and compared. If this fails: - * Each object returned by the parser is compared to the equivalent object tracked in the test. - * if the obejcts do not match the test fails - * Files are output with the content of each object. - * If the parser returns more or fewer objects than expected, the test fails. - -## Updating tests - -### Files -* ```parser_v200_verify_test.go``` contains the tests -* ```test-utils.go``` provides property agnostic functions for use by the tests and other utils -* ```-test-utils.go``` for example ```command-test-utils.go```, provides property related functions - -### Adding, modifying attributes of existing properties. - -In the ```-test-utils.go``` files there are: -* ```setValues``` functions. - * for example in ```command-test-utils.go``` : - * ```func setExecCommandValues(execCommand *schema.ExecCommand)``` - * ```func setCompositeCommandValues(compositeCommand *schema.CompositeCommand)``` -* These may use utility functions to set more complex attributes. -* Modify these functions to add/modify test for new/changed attributes. - -### Add new item to an existing property. - -For example add support for apply command to existing command support: - -1. In ```command-test-utils.go``` - * add functions: - * ```func (devfile *TestDevfile) setApplyCommandValues(applyCommand *schema.Command)``` - * randomly sets attribute values in the provided apply command object - * ```func (devfile *TestDevfile) createApplyCommand() *schema.ApplyCommand``` - * creates an empty apply command object and adds it to parser and test schema data - * follow the implementation of other similar functions. - * modify: - * ```func (devfile *TestDevfile) AddCommand(commandType schema.CommandType) schema.Command``` - * add logic to call createApplyCommand if commandType indicates such and call setApplyCommandValues - * ```func (devfile *TestDevfile) UpdateCommand(command *schema.Command) error``` - * add logic to call setApplyCommandValues if commandType indicates such. -1. In ```parser_v200_verify_test.go``` - * add new tests. for example: - * Test_ApplyCommand - CreateWithParser set to false, EditContent set to false - * Test_CreateApplyCommand - CreateWithParser set to true, EditContent set to false - * Test_EditApplyCommand - CreateWithParser set to false, EditContent set to true - * Test_CreateEditApplyCommand - CreateWithParser set to true, EditContent set to true - * modify existing test to include Apply commands - * Test_MultiCommand - * Test_Everything - -### Add new property - -Using existing support for commands as an illustration, any new property support added should follow the same structure: - -1. ```command-test-utils.go```: - * Specific to commands - * Commands require support for 5 different command types: - * Exec - * Apply (to be implemented) - * Composite - * VSCodeLaunch (to be implemented) - * VSCodeTask (to be implemented) - * Each of these command-types have equivalent functions: - * ```func (devfile *TestDevfile) createCommand() *schema.Command``` - * creates the command object and calls ```setCommandValues``` to add attribute values - * for example see: ```func (devfile *TestDevfile) createExecCommand() *schema.Command``` - * ```func (devfile *TestDevfile) setCommandValues(command *schema.Command)``` - * sets random attributes into the provided object - * for example see: ```func (devfile *TestDevfile) setExecCommandValues(ommand *schema.Command)``` - * Functions general to all commands - * ```func addCommand(genericCommand *GenericCommand) schema.Command``` - * includes logic to call the ```createCommand``` function for the command-Type of the supplied command object. - * main entry point for a test to add a command - * ```func (devfile *TestDevfile) UpdateCommand(command *schema.Command) error``` - * includes logic to call setCommandValues for each commandType. - * ```func (devfile TestDevfile) VerifyCommands(parserCommands []schema.Command) error``` - * includes logic to compare the array of commands obtained from the parser with those created by the test. if the compare fails: - * each individual command is compared. - * if a command compare fails, the parser version and test version of the command are oputput as yaml files to the tmp directory - * a check is made to determine if the parser returned a command not known to the test or the pasrer omitted a command expected by the test. -1. ```test-utils.go``` - * ```func (devfile TestDevfile) Verify()``` - * includes code to get object from the paser and verify their content. - * for commands code is required to: - 1. Retrieve each command from the parser - 1. Use command Id to obtain the GenericCommand object which matches - 1. Compare the command structure returned by the parser with the command structure saved in the GenericCommand object. - * ```func (devfile TestDevfile) EditCommands() error``` - * specific to command objects. - 1. Ensure devfile is written to disk - 1. Use parser to read devfile and get all command object - 1. For each command call: - * ```func (devfile *TestDevfile) UpdateCommand(command *schema.Command) error``` - 1. When all commands have been updated, use parser to write the updated devfile to disk -1. ```parser-v200-test.go``` - * ```type TestContent struct``` - * includes an array of command types: ```CommandTypes []schema.CommandType``` - * ```func Test_ExecCommand(t *testing.T)``` - 1. Creates a TestContent object - 1. Adds a single entry array containg schema.ExecCommandType to the array of command types - 1. Calls runTest for a single thread test - 1. Calls runMultiThreadTest for a multi-thread test. - * See also - * ```func Test_ExecCommand(t *testing.T)``` - * ```func Test_MultiCommand(t *testing.T)``` - * ```func Test_Everything(t *testing.T)``` - * Add logic to ```func runTest(testContent TestContent, t *testing.T)``` - 1. Add commands to the test. - 2. Start edits of commands if required. - - -#### Code flow - -Create, modify and verify an exec command: -1. parser_v200_verify_test.Test_ExecCommand - 1. parser-v200-test.runTest - 1. command-test-utils.AddCommand - 1. command-test-utils.createExecCommand - 1. command-test-utils.setExecCommandValues - 1. test-utils.WriteDevfile - 1. test-utils.EditCommands - 1. command-test-utils.UpdateCommand - 1. command-test-utils.setExecCommandValues - 1. test-utils.Verify - 1. command-test-utils.VerifyCommands - - + 1. ```./tmp/library_test/Test_*.yaml``` are the devfiles which are randomly generated at runtime. The file name matches the name of the test function which resulted in them being created. + 1. If a test detects an error when comparing properties returned by the parser with expected properties + * ```./tmp/library_test/Test_*__Parser.yaml``` - property as returned by the parser + * ```./tmp/library_test/Test_*__Test.yaml``` - property as expected by the test +Note: each run of the test removes the existing conents of the ```./tmp``` directory diff --git a/tests/v2/libraryTest/library_test.go b/tests/v2/libraryTest/library_test.go new file mode 100644 index 00000000..7dc6deab --- /dev/null +++ b/tests/v2/libraryTest/library_test.go @@ -0,0 +1,135 @@ +package api + +import ( + "testing" + + schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + commonUtils "github.com/devfile/api/v2/test/v200/utils/common" + libraryUtils "github.com/devfile/library/tests/v2/utils/library" +) + +// TestContent - structure used by a test to configure the tests to run +type TestContent struct { + CommandTypes []schema.CommandType + ComponentTypes []schema.ComponentType + FileName string + EditContent bool +} + +func Test_ExecCommand(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType} + testContent.EditContent = false + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} +func Test_ExecCommandEdit(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType} + testContent.EditContent = true + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_ApplyCommand(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ApplyCommandType} + testContent.EditContent = false + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_ApplyCommandEdit(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ApplyCommandType} + testContent.EditContent = true + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_CompositeCommand(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.CompositeCommandType} + testContent.EditContent = false + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} +func Test_CompositeCommandEdit(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.CompositeCommandType} + testContent.EditContent = true + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_MultiCommand(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType, + schema.CompositeCommandType, + schema.ApplyCommandType} + testContent.EditContent = true + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_ContainerComponent(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType} + testContent.EditContent = false + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_ContainerComponentEdit(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType} + testContent.EditContent = true + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_VolumeComponent(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.VolumeComponentType} + testContent.EditContent = false + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_VolumeComponentEdit(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.VolumeComponentType} + testContent.EditContent = true + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_MultiComponent(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType, schema.VolumeComponentType} + testContent.EditContent = true + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} + +func Test_Everything(t *testing.T) { + testContent := commonUtils.TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType, schema.CompositeCommandType, schema.ApplyCommandType} + testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType, schema.VolumeComponentType} + testContent.EditContent = true + testContent.FileName = commonUtils.GetDevFileName() + libraryUtils.RunTest(testContent, t) + libraryUtils.RunMultiThreadTest(testContent, t) +} diff --git a/tests/v2/parserTest/parser_api_test.go b/tests/v2/parserTest/parser_api_test.go deleted file mode 100644 index ead6bb53..00000000 --- a/tests/v2/parserTest/parser_api_test.go +++ /dev/null @@ -1,200 +0,0 @@ -package api - -import ( - "fmt" - "strconv" - "testing" - "time" - - "github.com/devfile/library/tests/v2/utils" - - schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" -) - -const ( - // numThreads : Number of threads used by multi-thread tests - numThreads = 5 - // maxCommands : The maximum number of commands to include in a generated devfile - maxCommands = 10 - // maxComponents : The maximum number of components to include in a generated devfile - maxComponents = 10 -) - -// TestContent - structure used by a test to configure the tests to run -type TestContent struct { - CommandTypes []schema.CommandType - ComponentTypes []schema.ComponentType - FileName string - EditContent bool -} - -func Test_ExecCommand(t *testing.T) { - testContent := TestContent{} - testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType} - testContent.EditContent = false - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} -func Test_ExecCommandEdit(t *testing.T) { - testContent := TestContent{} - testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType} - testContent.EditContent = true - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} - -func Test_CompositeCommand(t *testing.T) { - testContent := TestContent{} - testContent.CommandTypes = []schema.CommandType{schema.CompositeCommandType} - testContent.EditContent = false - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} -func Test_CompositeCommandEdit(t *testing.T) { - testContent := TestContent{} - testContent.CommandTypes = []schema.CommandType{schema.CompositeCommandType} - testContent.EditContent = true - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} - -func Test_MultiCommand(t *testing.T) { - testContent := TestContent{} - testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType, - schema.CompositeCommandType} - testContent.EditContent = true - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} - -func Test_ContainerComponent(t *testing.T) { - testContent := TestContent{} - testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType} - testContent.EditContent = false - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} - -func Test_ContainerComponentEdit(t *testing.T) { - testContent := TestContent{} - testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType} - testContent.EditContent = true - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} - -func Test_VolumeComponent(t *testing.T) { - testContent := TestContent{} - testContent.ComponentTypes = []schema.ComponentType{schema.VolumeComponentType} - testContent.EditContent = false - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} - -func Test_VolumeComponentEdit(t *testing.T) { - testContent := TestContent{} - testContent.ComponentTypes = []schema.ComponentType{schema.VolumeComponentType} - testContent.EditContent = true - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} - -func Test_MultiComponent(t *testing.T) { - testContent := TestContent{} - testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType, schema.VolumeComponentType} - testContent.EditContent = true - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} - -func Test_Everything(t *testing.T) { - testContent := TestContent{} - testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType, schema.CompositeCommandType} - testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType, schema.VolumeComponentType} - testContent.EditContent = true - testContent.FileName = utils.GetDevFileName() - runTest(testContent, t) - runMultiThreadTest(testContent, t) -} - -// runMultiThreadTest : Runs the same test on multiple threads, the test is based on the content of the specified TestContent -func runMultiThreadTest(testContent TestContent, t *testing.T) { - - utils.LogMessage(fmt.Sprintf("Start Threaded test for %s", testContent.FileName)) - - devfileName := testContent.FileName - var i int - for i = 1; i < numThreads; i++ { - testContent.FileName = utils.AddSuffixToFileName(devfileName, strconv.Itoa(i)) - go runTest(testContent, t) - } - testContent.FileName = utils.AddSuffixToFileName(devfileName, strconv.Itoa(i)) - runTest(testContent, t) - - utils.LogMessage(fmt.Sprintf("Sleep 3 seconds to allow all threads to complete : %s", devfileName)) - time.Sleep(3 * time.Second) - utils.LogMessage(fmt.Sprintf("Sleep complete : %s", devfileName)) - -} - -// runTest : Runs a test beased on the content of the specified TestContent -func runTest(testContent TestContent, t *testing.T) { - - utils.LogMessage(fmt.Sprintf("Start test for %s", testContent.FileName)) - testDevfile, err := utils.GetDevfile(testContent.FileName) - if err != nil { - t.Fatalf(utils.LogMessage(fmt.Sprintf("Error creating devfile : %v", err))) - } - - if len(testContent.CommandTypes) > 0 { - numCommands := utils.GetRandomNumber(maxCommands) - for i := 0; i < numCommands; i++ { - commandIndex := utils.GetRandomNumber(len(testContent.CommandTypes)) - testDevfile.AddCommand(testContent.CommandTypes[commandIndex-1]) - } - } - - if len(testContent.ComponentTypes) > 0 { - numComponents := utils.GetRandomNumber(maxComponents) - for i := 0; i < numComponents; i++ { - componentIndex := utils.GetRandomNumber(len(testContent.ComponentTypes)) - testDevfile.AddComponent(testContent.ComponentTypes[componentIndex-1]) - } - } - - err = testDevfile.WriteDevfile(utils.GetBinaryDecision()) - if err != nil { - t.Fatalf(utils.LogErrorMessage(fmt.Sprintf("ERROR creating devfile : %s : %v", testContent.FileName, err))) - } - - if testContent.EditContent { - if len(testContent.CommandTypes) > 0 { - err = testDevfile.EditCommands() - if err != nil { - t.Fatalf(utils.LogErrorMessage(fmt.Sprintf("ERROR editing commands : %s : %v", testContent.FileName, err))) - } - } - if len(testContent.ComponentTypes) > 0 { - err = testDevfile.EditComponents() - if err != nil { - t.Fatalf(utils.LogErrorMessage(fmt.Sprintf("ERROR editing components : %s : %v", testContent.FileName, err))) - } - } - } - - err = testDevfile.Verify() - if err != nil { - t.Fatalf(utils.LogErrorMessage(fmt.Sprintf("ERROR verifying devfile content : %s : %v", testContent.FileName, err))) - } - -} diff --git a/tests/v2/utils/command_test_utils.go b/tests/v2/utils/command_test_utils.go deleted file mode 100644 index 8be1ea5e..00000000 --- a/tests/v2/utils/command_test_utils.go +++ /dev/null @@ -1,270 +0,0 @@ -package utils - -import ( - "errors" - "fmt" - "io/ioutil" - - "github.com/google/go-cmp/cmp" - "sigs.k8s.io/yaml" - - schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" -) - -// commandAdded adds a new command to the test schema data and to the parser data -func (devfile *TestDevfile) commandAdded(command schema.Command) { - LogInfoMessage(fmt.Sprintf("command added Id: %s", command.Id)) - devfile.SchemaDevFile.Commands = append(devfile.SchemaDevFile.Commands, command) - devfile.ParserData.AddCommands(command) -} - -// commandUpdated updates a command in the parser data -func (devfile *TestDevfile) commandUpdated(command schema.Command) { - LogInfoMessage(fmt.Sprintf("command updated Id: %s", command.Id)) - devfile.ParserData.UpdateCommand(command) -} - -// addEnv creates and returns a specifed number of env attributes in a schema structure -func addEnv(numEnv int) []schema.EnvVar { - commandEnvs := make([]schema.EnvVar, numEnv) - for i := 0; i < numEnv; i++ { - commandEnvs[i].Name = "Name_" + GetRandomString(5, false) - commandEnvs[i].Value = "Value_" + GetRandomString(5, false) - LogInfoMessage(fmt.Sprintf("Add Env: %s", commandEnvs[i])) - } - return commandEnvs -} - -// addAttributes creates returns a specifed number of attributes in a schema structure -func addAttributes(numAtrributes int) map[string]string { - attributes := make(map[string]string) - for i := 0; i < numAtrributes; i++ { - AttributeName := "Name_" + GetRandomString(6, false) - attributes[AttributeName] = "Value_" + GetRandomString(6, false) - LogInfoMessage(fmt.Sprintf("Add attribute : %s = %s", AttributeName, attributes[AttributeName])) - } - return attributes -} - -// addGroup creates and returns a group in a schema structure -func (devfile *TestDevfile) addGroup() *schema.CommandGroup { - - commandGroup := schema.CommandGroup{} - commandGroup.Kind = GetRandomGroupKind() - LogInfoMessage(fmt.Sprintf("group Kind: %s, default already set %t", commandGroup.Kind, devfile.GroupDefaults[commandGroup.Kind])) - // Ensure only one and at least one of each type are labelled as default - if !devfile.GroupDefaults[commandGroup.Kind] { - devfile.GroupDefaults[commandGroup.Kind] = true - commandGroup.IsDefault = true - } else { - commandGroup.IsDefault = false - } - LogInfoMessage(fmt.Sprintf("group isDefault: %t", commandGroup.IsDefault)) - return &commandGroup -} - -// AddCommand creates a command of a specified type in a schema structure and pupulates it with random attributes -func (devfile *TestDevfile) AddCommand(commandType schema.CommandType) schema.Command { - - var command *schema.Command - if commandType == schema.ExecCommandType { - command = devfile.createExecCommand() - devfile.setExecCommandValues(command) - // command must be mentioned by a container component - command.Exec.Component = devfile.GetContainerName() - } else if commandType == schema.CompositeCommandType { - command = devfile.createCompositeCommand() - devfile.setCompositeCommandValues(command) - } - return *command -} - -// UpdateCommand randomly updates attribute values of a specified command in the devfile schema -func (devfile *TestDevfile) UpdateCommand(commandId string) error { - - var err error - testCommand, found := getSchemaCommand(devfile.SchemaDevFile.Commands, commandId) - if found { - LogInfoMessage(fmt.Sprintf("Updating command id: %s", commandId)) - if testCommand.Exec != nil { - devfile.setExecCommandValues(testCommand) - } else if testCommand.Composite != nil { - devfile.setCompositeCommandValues(testCommand) - } - } else { - err = errors.New(LogErrorMessage(fmt.Sprintf("Command not found in test : %s", commandId))) - } - return err -} - -// createExecCommand creates and returns an empty exec command in a schema structure -func (devfile *TestDevfile) createExecCommand() *schema.Command { - - LogInfoMessage("Create an exec command :") - command := schema.Command{} - command.Id = GetRandomUniqueString(8, true) - LogInfoMessage(fmt.Sprintf("command Id: %s", command.Id)) - command.Exec = &schema.ExecCommand{} - devfile.commandAdded(command) - return &command - -} - -// setExecCommandValues randomly sets exec command attribute to random values -func (devfile *TestDevfile) setExecCommandValues(command *schema.Command) { - - execCommand := command.Exec - execCommand.CommandLine = GetRandomString(4, false) + " " + GetRandomString(4, false) - LogInfoMessage(fmt.Sprintf("....... commandLine: %s", execCommand.CommandLine)) - - // If group already leave it to make sure defaults are not deleted or added - if execCommand.Group == nil { - if GetRandomDecision(2, 1) { - execCommand.Group = devfile.addGroup() - } - } - - if GetBinaryDecision() { - execCommand.Label = GetRandomString(12, false) - LogInfoMessage(fmt.Sprintf("....... label: %s", execCommand.Label)) - } else { - execCommand.Label = "" - } - - if GetBinaryDecision() { - execCommand.WorkingDir = "./tmp" - LogInfoMessage(fmt.Sprintf("....... WorkingDir: %s", execCommand.WorkingDir)) - } else { - execCommand.WorkingDir = "" - } - - execCommand.HotReloadCapable = GetBinaryDecision() - LogInfoMessage(fmt.Sprintf("....... HotReloadCapable: %t", execCommand.HotReloadCapable)) - - if GetBinaryDecision() { - execCommand.Env = addEnv(GetRandomNumber(4)) - } else { - execCommand.Env = nil - } - devfile.commandUpdated(*command) - -} - -// getSchemaCommand get a specified command from the devfile schema structure -func getSchemaCommand(commands []schema.Command, id string) (*schema.Command, bool) { - found := false - var schemaCommand schema.Command - for _, command := range commands { - if command.Id == id { - schemaCommand = command - found = true - break - } - } - return &schemaCommand, found -} - -// createCompositeCommand creates an empty composite command in a schema structure -func (devfile *TestDevfile) createCompositeCommand() *schema.Command { - - LogInfoMessage("Create a composite command :") - command := schema.Command{} - command.Id = GetRandomUniqueString(8, true) - LogInfoMessage(fmt.Sprintf("command Id: %s", command.Id)) - command.Composite = &schema.CompositeCommand{} - devfile.commandAdded(command) - - return &command -} - -// setCompositeCommandValues randomly sets composite command attribute to random values -func (devfile *TestDevfile) setCompositeCommandValues(command *schema.Command) { - - compositeCommand := command.Composite - numCommands := GetRandomNumber(3) - - for i := 0; i < numCommands; i++ { - execCommand := devfile.AddCommand(schema.ExecCommandType) - compositeCommand.Commands = append(compositeCommand.Commands, execCommand.Id) - LogInfoMessage(fmt.Sprintf("....... command %d of %d : %s", i, numCommands, execCommand.Id)) - } - - // If group already exists - leave it to make sure defaults are not deleted or added - if compositeCommand.Group == nil { - if GetRandomDecision(2, 1) { - compositeCommand.Group = devfile.addGroup() - } - } - - if GetBinaryDecision() { - compositeCommand.Label = GetRandomString(12, false) - LogInfoMessage(fmt.Sprintf("....... label: %s", compositeCommand.Label)) - } - - if GetBinaryDecision() { - compositeCommand.Parallel = true - LogInfoMessage(fmt.Sprintf("....... Parallel: %t", compositeCommand.Parallel)) - } - - devfile.commandUpdated(*command) -} - -// VerifyCommands verifies commands returned by the parser are the same as those saved in the devfile schema -func (devfile *TestDevfile) VerifyCommands(parserCommands []schema.Command) error { - - LogInfoMessage("Enter VerifyCommands") - var errorString []string - - // Compare entire array of commands - if !cmp.Equal(parserCommands, devfile.SchemaDevFile.Commands) { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("Command array compare failed."))) - // Array compare failed. Narrow down by comparing indivdual commands - for _, command := range parserCommands { - if testCommand, found := getSchemaCommand(devfile.SchemaDevFile.Commands, command.Id); found { - if !cmp.Equal(command, *testCommand) { - parserFilename := AddSuffixToFileName(devfile.FileName, "_"+command.Id+"_Parser") - testFilename := AddSuffixToFileName(devfile.FileName, "_"+command.Id+"_Test") - LogInfoMessage(fmt.Sprintf(".......marshall and write devfile %s", devfile.FileName)) - c, err := yaml.Marshal(command) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf(".......marshall devfile %s", parserFilename))) - } else { - err = ioutil.WriteFile(parserFilename, c, 0644) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf(".......write devfile %s", parserFilename))) - } - } - LogInfoMessage(fmt.Sprintf(".......marshall and write devfile %s", testFilename)) - c, err = yaml.Marshal(testCommand) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf(".......marshall devfile %s", testFilename))) - } else { - err = ioutil.WriteFile(testFilename, c, 0644) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf(".......write devfile %s", testFilename))) - } - } - errorString = append(errorString, LogInfoMessage(fmt.Sprintf("Command %s did not match, see files : %s and %s", command.Id, parserFilename, testFilename))) - } else { - LogInfoMessage(fmt.Sprintf(" --> Command matched : %s", command.Id)) - } - } else { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("Command from parser not known to test - id : %s ", command.Id))) - } - - } - for _, command := range devfile.SchemaDevFile.Commands { - if _, found := getSchemaCommand(parserCommands, command.Id); !found { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("Command from test not returned by parser : %s ", command.Id))) - } - } - } else { - LogInfoMessage(fmt.Sprintf(" --> Command structures matched")) - } - - var err error - if len(errorString) > 0 { - err = errors.New(fmt.Sprint(errorString)) - } - return err -} diff --git a/tests/v2/utils/component_test_utils.go b/tests/v2/utils/component_test_utils.go deleted file mode 100644 index cfb05cb4..00000000 --- a/tests/v2/utils/component_test_utils.go +++ /dev/null @@ -1,267 +0,0 @@ -package utils - -import ( - "errors" - "fmt" - "io/ioutil" - "strconv" - - schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - "github.com/google/go-cmp/cmp" - "sigs.k8s.io/yaml" -) - -// componentAdded adds a new component to the test schema data and to the parser data -func (devfile *TestDevfile) componentAdded(component schema.Component) { - LogInfoMessage(fmt.Sprintf("component added Name: %s", component.Name)) - devfile.SchemaDevFile.Components = append(devfile.SchemaDevFile.Components, component) - devfile.ParserData.AddComponents([]schema.Component{component}) -} - -// componetUpdated updates a component in the parser data -func (devfile *TestDevfile) componentUpdated(component schema.Component) { - LogInfoMessage(fmt.Sprintf("component updated Name: %s", component.Name)) - devfile.ParserData.UpdateComponent(component) -} - -// addVolume returns volumeMounts in a schema structure based on a specified number of volumes -func (devfile *TestDevfile) addVolume(numVols int) []schema.VolumeMount { - commandVols := make([]schema.VolumeMount, numVols) - for i := 0; i < numVols; i++ { - volumeComponent := devfile.AddComponent(schema.VolumeComponentType) - commandVols[i].Name = volumeComponent.Name - commandVols[i].Path = "/Path_" + GetRandomString(5, false) - LogInfoMessage(fmt.Sprintf("....... Add Volume: %s", commandVols[i])) - } - return commandVols -} - -// getSchemaComponent returns a named component from an array of components -func getSchemaComponent(components []schema.Component, name string) (*schema.Component, bool) { - found := false - var schemaComponent schema.Component - for _, component := range components { - if component.Name == name { - schemaComponent = component - found = true - break - } - } - return &schemaComponent, found -} - -// AddComponent adds a component of the specified type, with random attributes, to the devfile schema -func (devfile *TestDevfile) AddComponent(componentType schema.ComponentType) schema.Component { - - var component schema.Component - if componentType == schema.ContainerComponentType { - component = devfile.createContainerComponent() - devfile.setContainerComponentValues(&component) - } else if componentType == schema.VolumeComponentType { - component = devfile.createVolumeComponent() - devfile.setVolumeComponentValues(&component) - } - return component -} - -// createContainerComponent creates a container component, ready for attribute setting -func (devfile *TestDevfile) createContainerComponent() schema.Component { - - LogInfoMessage("Create a container component :") - component := schema.Component{} - component.Name = GetRandomUniqueString(8, true) - LogInfoMessage(fmt.Sprintf("....... Name: %s", component.Name)) - component.Container = &schema.ContainerComponent{} - devfile.componentAdded(component) - return component - -} - -// createVolumeComponent creates a volume component , ready for attribute setting -func (devfile *TestDevfile) createVolumeComponent() schema.Component { - - LogInfoMessage("Create a volume component :") - component := schema.Component{} - component.Name = GetRandomUniqueString(8, true) - LogInfoMessage(fmt.Sprintf("....... Name: %s", component.Name)) - component.Volume = &schema.VolumeComponent{} - devfile.componentAdded(component) - return component - -} - -// GetContainer returns the name of an existing, or newly created, container. -func (devfile *TestDevfile) GetContainerName() string { - - componentName := "" - for _, currentComponent := range devfile.SchemaDevFile.Components { - if currentComponent.Container != nil { - componentName = currentComponent.Name - LogInfoMessage(fmt.Sprintf("return existing container from GetContainerName : %s", componentName)) - break - } - } - - if componentName == "" { - component := devfile.createContainerComponent() - componentName = component.Name - LogInfoMessage(fmt.Sprintf("retrun new container from GetContainerName : %s", componentName)) - } - - return componentName -} - -// setContainerComponentValues randomly sets container component attributes to random values -func (devfile *TestDevfile) setContainerComponentValues(component *schema.Component) { - - containerComponent := component.Container - - containerComponent.Image = GetRandomUniqueString(8+GetRandomNumber(10), false) - - if GetBinaryDecision() { - numCommands := GetRandomNumber(3) - containerComponent.Command = make([]string, numCommands) - for i := 0; i < numCommands; i++ { - containerComponent.Command[i] = GetRandomString(4+GetRandomNumber(10), false) - LogInfoMessage(fmt.Sprintf("....... command %d of %d : %s", i, numCommands, containerComponent.Command[i])) - } - } - - if GetBinaryDecision() { - numArgs := GetRandomNumber(3) - containerComponent.Args = make([]string, numArgs) - for i := 0; i < numArgs; i++ { - containerComponent.Args[i] = GetRandomString(8+GetRandomNumber(10), false) - LogInfoMessage(fmt.Sprintf("....... arg %d of %d : %s", i, numArgs, containerComponent.Args[i])) - } - } - - containerComponent.DedicatedPod = GetBinaryDecision() - LogInfoMessage(fmt.Sprintf("....... DedicatedPod: %t", containerComponent.DedicatedPod)) - - if GetBinaryDecision() { - containerComponent.MemoryLimit = strconv.Itoa(4+GetRandomNumber(124)) + "M" - LogInfoMessage(fmt.Sprintf("....... MemoryLimit: %s", containerComponent.MemoryLimit)) - } - - if GetBinaryDecision() { - setMountSources := GetBinaryDecision() - containerComponent.MountSources = &setMountSources - LogInfoMessage(fmt.Sprintf("....... MountSources: %t", *containerComponent.MountSources)) - - if setMountSources { - containerComponent.SourceMapping = "/" + GetRandomString(8, false) - LogInfoMessage(fmt.Sprintf("....... SourceMapping: %s", containerComponent.SourceMapping)) - } - } - - if GetBinaryDecision() { - containerComponent.Env = addEnv(GetRandomNumber(4)) - } else { - containerComponent.Env = nil - } - - if len(containerComponent.VolumeMounts) == 0 { - if GetBinaryDecision() { - containerComponent.VolumeMounts = devfile.addVolume(GetRandomNumber(4)) - } - } - - if GetBinaryDecision() { - containerComponent.Endpoints = devfile.CreateEndpoints() - } - - devfile.componentUpdated(*component) - -} - -// setVolumeComponentValues randomly sets volume component attributes to random values -func (devfile *TestDevfile) setVolumeComponentValues(component *schema.Component) { - - component.Volume.Size = strconv.Itoa(4+GetRandomNumber(252)) + "G" - LogInfoMessage(fmt.Sprintf("....... volumeComponent.Size: %s", component.Volume.Size)) - devfile.componentUpdated(*component) - -} - -// UpdateComponent randomly updates the attribute values of a specified component -func (devfile *TestDevfile) UpdateComponent(componentName string) error { - - var errorString []string - testComponent, found := getSchemaComponent(devfile.SchemaDevFile.Components, componentName) - if found { - LogInfoMessage(fmt.Sprintf("....... Updating component name: %s", componentName)) - if testComponent.Container != nil { - devfile.setContainerComponentValues(testComponent) - } else if testComponent.Volume != nil { - devfile.setVolumeComponentValues(testComponent) - } else { - errorString = append(errorString, LogInfoMessage(fmt.Sprintf("....... Component is not of expected type."))) - } - } else { - errorString = append(errorString, LogInfoMessage(fmt.Sprintf("....... Component not found in test : %s", componentName))) - } - var err error - if len(errorString) > 0 { - err = errors.New(fmt.Sprint(errorString)) - } - return err -} - -// VerifyComponents verifies components returned by the parser are the same as those saved in the devfile schema -func (devfile *TestDevfile) VerifyComponents(parserComponents []schema.Component) error { - - LogInfoMessage("Enter VerifyComponents") - var errorString []string - - // Compare entire array of components - if !cmp.Equal(parserComponents, devfile.SchemaDevFile.Components) { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("Component array compare failed."))) - for _, component := range parserComponents { - if testComponent, found := getSchemaComponent(devfile.SchemaDevFile.Components, component.Name); found { - if !cmp.Equal(component, *testComponent) { - parserFilename := AddSuffixToFileName(devfile.FileName, "_"+component.Name+"_Parser") - testFilename := AddSuffixToFileName(devfile.FileName, "_"+component.Name+"_Test") - LogInfoMessage(fmt.Sprintf(".......marshall and write devfile %s", parserFilename)) - c, err := yaml.Marshal(component) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf(".......marshall devfile %s", parserFilename))) - } else { - err = ioutil.WriteFile(parserFilename, c, 0644) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf(".......write devfile %s", parserFilename))) - } - } - LogInfoMessage(fmt.Sprintf(".......marshall and write devfile %s", testFilename)) - c, err = yaml.Marshal(testComponent) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf(".......marshall devfile %s", testFilename))) - } else { - err = ioutil.WriteFile(testFilename, c, 0644) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf(".......write devfile %s", testFilename))) - } - } - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("Component %s did not match, see files : %s and %s", component.Name, parserFilename, testFilename))) - } else { - LogInfoMessage(fmt.Sprintf(" --> Component matched : %s", component.Name)) - } - } else { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("Component from parser not known to test - id : %s ", component.Name))) - } - } - for _, component := range devfile.SchemaDevFile.Components { - if _, found := getSchemaComponent(parserComponents, component.Name); !found { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("Component from test not returned by parser : %s ", component.Name))) - } - } - } else { - LogInfoMessage(fmt.Sprintf("Component structures matched")) - } - - var err error - if len(errorString) > 0 { - err = errors.New(fmt.Sprint(errorString)) - } - return err -} diff --git a/tests/v2/utils/endpoint-test-utils.go b/tests/v2/utils/endpoint-test-utils.go deleted file mode 100644 index 02d3f0a8..00000000 --- a/tests/v2/utils/endpoint-test-utils.go +++ /dev/null @@ -1,85 +0,0 @@ -package utils - -import ( - "fmt" - - schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" -) - -var Exposures = [...]schema.EndpointExposure{schema.PublicEndpointExposure, schema.InternalEndpointExposure, schema.NoneEndpointExposure} - -// getRandomExposure returns a random exposure value -func getRandomExposure() schema.EndpointExposure { - return Exposures[GetRandomNumber(len(Exposures))-1] -} - -//var Protocols = [...]schema.EndpointProtocol{schema.HTTPEndpointProtocol, schema.HTTPSEndpointProtocol, schema.WSEndpointProtocol, schema.WSSEndpointProtocol, schema.TCPEndpointProtocol, schema.UDPEndpointProtocol} -var Protocols = [...]schema.EndpointProtocol{schema.HTTPEndpointProtocol, schema.WSEndpointProtocol, schema.TCPEndpointProtocol, schema.UDPEndpointProtocol} - -// getRandomProtocol returns a random protocol value -func getRandomProtocol() schema.EndpointProtocol { - return Protocols[GetRandomNumber(len(Protocols))-1] -} - -// getUniquePort return a port value not previously used in that same devfile -func (devfile *TestDevfile) getUniquePort() int { - - // max sure a lot of unique ports exist - maxPorts := len(devfile.UsedPorts) + 5000 - - var port int - used := true - for used { - port = GetRandomNumber(maxPorts) - _, used = devfile.UsedPorts[port] - } - devfile.UsedPorts[port] = true - return port -} - -// CreateEndpoints creates and returns a randon number of endpoints in a schema structure -func (devfile *TestDevfile) CreateEndpoints() []schema.Endpoint { - - numEndpoints := GetRandomNumber(5) - endpoints := make([]schema.Endpoint, numEndpoints) - - commonPort := devfile.getUniquePort() - - for i := 0; i < numEndpoints; i++ { - - endpoint := schema.Endpoint{} - - endpoint.Name = GetRandomUniqueString(GetRandomNumber(15)+5, true) - LogInfoMessage(fmt.Sprintf(" ....... add endpoint %d name : %s", i, endpoint.Name)) - - if GetBinaryDecision() { - endpoint.TargetPort = devfile.getUniquePort() - } else { - endpoint.TargetPort = commonPort - } - LogInfoMessage(fmt.Sprintf(" ....... add endpoint %d targetPort: %d", i, endpoint.TargetPort)) - - if GetBinaryDecision() { - endpoint.Exposure = getRandomExposure() - LogInfoMessage(fmt.Sprintf(" ....... add endpoint %d exposure: %s", i, endpoint.Exposure)) - } - - if GetBinaryDecision() { - endpoint.Protocol = getRandomProtocol() - LogInfoMessage(fmt.Sprintf(" ....... add endpoint %d protocol: %s", i, endpoint.Protocol)) - } - - endpoint.Secure = GetBinaryDecision() - LogInfoMessage(fmt.Sprintf(" ....... add endpoint %d secure: %t", i, endpoint.Secure)) - - if GetBinaryDecision() { - endpoint.Path = "/Path_" + GetRandomString(GetRandomNumber(10)+3, false) - LogInfoMessage(fmt.Sprintf(" ....... add endpoint %d path: %s", i, endpoint.Path)) - } - - endpoints[i] = endpoint - - } - - return endpoints -} diff --git a/tests/v2/utils/library/command_test_utils.go b/tests/v2/utils/library/command_test_utils.go new file mode 100644 index 00000000..2a539b1b --- /dev/null +++ b/tests/v2/utils/library/command_test_utils.go @@ -0,0 +1,107 @@ +package utils + +import ( + "errors" + "fmt" + "io/ioutil" + + "github.com/google/go-cmp/cmp" + "sigs.k8s.io/yaml" + + schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + commonUtils "github.com/devfile/api/v2/test/v200/utils/common" +) + +// getSchemaCommand get a specified command from the devfile schema structure +func getSchemaCommand(commands []schema.Command, id string) (*schema.Command, bool) { + found := false + var schemaCommand schema.Command + for _, command := range commands { + if command.Id == id { + schemaCommand = command + found = true + break + } + } + return &schemaCommand, found +} + +// UpdateCommand randomly updates attribute values of a specified command in the devfile schema +func UpdateCommand(devfile *commonUtils.TestDevfile, commandId string) error { + + var err error + testCommand, found := getSchemaCommand(devfile.SchemaDevFile.Commands, commandId) + if found { + commonUtils.LogInfoMessage(fmt.Sprintf("Updating command id: %s", commandId)) + if testCommand.Exec != nil { + devfile.SetExecCommandValues(testCommand) + } else if testCommand.Composite != nil { + devfile.SetCompositeCommandValues(testCommand) + } else if testCommand.Apply != nil { + devfile.SetApplyCommandValues(testCommand) + } + } else { + err = errors.New(commonUtils.LogErrorMessage(fmt.Sprintf("Command not found in test : %s", commandId))) + } + return err +} + +// VerifyCommands verifies commands returned by the parser are the same as those saved in the devfile schema +func VerifyCommands(devfile *commonUtils.TestDevfile, parserCommands []schema.Command) error { + + commonUtils.LogInfoMessage("Enter VerifyCommands") + var errorString []string + + // Compare entire array of commands + if !cmp.Equal(parserCommands, devfile.SchemaDevFile.Commands) { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Command array compare failed."))) + // Array compare failed. Narrow down by comparing indivdual commands + for _, command := range parserCommands { + if testCommand, found := getSchemaCommand(devfile.SchemaDevFile.Commands, command.Id); found { + if !cmp.Equal(command, *testCommand) { + parserFilename := commonUtils.AddSuffixToFileName(devfile.FileName, "_"+command.Id+"_Parser") + testFilename := commonUtils.AddSuffixToFileName(devfile.FileName, "_"+command.Id+"_Test") + commonUtils.LogInfoMessage(fmt.Sprintf(".......marshall and write devfile %s", devfile.FileName)) + c, err := yaml.Marshal(command) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf(".......marshall devfile %s", parserFilename))) + } else { + err = ioutil.WriteFile(parserFilename, c, 0644) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf(".......write devfile %s", parserFilename))) + } + } + commonUtils.LogInfoMessage(fmt.Sprintf(".......marshall and write devfile %s", testFilename)) + c, err = yaml.Marshal(testCommand) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf(".......marshall devfile %s", testFilename))) + } else { + err = ioutil.WriteFile(testFilename, c, 0644) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf(".......write devfile %s", testFilename))) + } + } + errorString = append(errorString, commonUtils.LogInfoMessage(fmt.Sprintf("Command %s did not match, see files : %s and %s", command.Id, parserFilename, testFilename))) + } else { + commonUtils.LogInfoMessage(fmt.Sprintf(" --> Command matched : %s", command.Id)) + } + } else { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Command from parser not known to test - id : %s ", command.Id))) + } + + } + for _, command := range devfile.SchemaDevFile.Commands { + if _, found := getSchemaCommand(parserCommands, command.Id); !found { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Command from test not returned by parser : %s ", command.Id))) + } + } + } else { + commonUtils.LogInfoMessage(fmt.Sprintf(" --> Command structures matched")) + } + + var err error + if len(errorString) > 0 { + err = errors.New(fmt.Sprint(errorString)) + } + return err +} diff --git a/tests/v2/utils/library/component_test_utils.go b/tests/v2/utils/library/component_test_utils.go new file mode 100644 index 00000000..89bf7b1b --- /dev/null +++ b/tests/v2/utils/library/component_test_utils.go @@ -0,0 +1,108 @@ +package utils + +import ( + "errors" + "fmt" + "io/ioutil" + + schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + commonUtils "github.com/devfile/api/v2/test/v200/utils/common" + "github.com/google/go-cmp/cmp" + "sigs.k8s.io/yaml" +) + +// getSchemaComponent returns a named component from an array of components +func getSchemaComponent(components []schema.Component, name string) (*schema.Component, bool) { + found := false + var schemaComponent schema.Component + for _, component := range components { + if component.Name == name { + schemaComponent = component + found = true + break + } + } + return &schemaComponent, found +} + +// UpdateComponent randomly updates the attribute values of a specified component +func UpdateComponent(devfile *commonUtils.TestDevfile, componentName string) error { + + var errorString []string + testComponent, found := getSchemaComponent(devfile.SchemaDevFile.Components, componentName) + if found { + commonUtils.LogInfoMessage(fmt.Sprintf("....... Updating component name: %s", componentName)) + if testComponent.Container != nil { + devfile.SetContainerComponentValues(testComponent) + } else if testComponent.Volume != nil { + devfile.SetVolumeComponentValues(testComponent) + } else { + errorString = append(errorString, commonUtils.LogInfoMessage(fmt.Sprintf("....... Component is not of expected type."))) + } + } else { + errorString = append(errorString, commonUtils.LogInfoMessage(fmt.Sprintf("....... Component not found in test : %s", componentName))) + } + var err error + if len(errorString) > 0 { + err = errors.New(fmt.Sprint(errorString)) + } + return err +} + +// VerifyComponents verifies components returned by the parser are the same as those saved in the devfile schema +func VerifyComponents(devfile *commonUtils.TestDevfile, parserComponents []schema.Component) error { + + commonUtils.LogInfoMessage("Enter VerifyComponents") + var errorString []string + + // Compare entire array of components + if !cmp.Equal(parserComponents, devfile.SchemaDevFile.Components) { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Component array compare failed."))) + for _, component := range parserComponents { + if testComponent, found := getSchemaComponent(devfile.SchemaDevFile.Components, component.Name); found { + if !cmp.Equal(component, *testComponent) { + parserFilename := commonUtils.AddSuffixToFileName(devfile.FileName, "_"+component.Name+"_Parser") + testFilename := commonUtils.AddSuffixToFileName(devfile.FileName, "_"+component.Name+"_Test") + commonUtils.LogInfoMessage(fmt.Sprintf(".......marshall and write devfile %s", parserFilename)) + c, err := yaml.Marshal(component) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf(".......marshall devfile %s", parserFilename))) + } else { + err = ioutil.WriteFile(parserFilename, c, 0644) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf(".......write devfile %s", parserFilename))) + } + } + commonUtils.LogInfoMessage(fmt.Sprintf(".......marshall and write devfile %s", testFilename)) + c, err = yaml.Marshal(testComponent) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf(".......marshall devfile %s", testFilename))) + } else { + err = ioutil.WriteFile(testFilename, c, 0644) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf(".......write devfile %s", testFilename))) + } + } + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Component %s did not match, see files : %s and %s", component.Name, parserFilename, testFilename))) + } else { + commonUtils.LogInfoMessage(fmt.Sprintf(" --> Component matched : %s", component.Name)) + } + } else { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Component from parser not known to test - id : %s ", component.Name))) + } + } + for _, component := range devfile.SchemaDevFile.Components { + if _, found := getSchemaComponent(parserComponents, component.Name); !found { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Component from test not returned by parser : %s ", component.Name))) + } + } + } else { + commonUtils.LogInfoMessage(fmt.Sprintf("Component structures matched")) + } + + var err error + if len(errorString) > 0 { + err = errors.New(fmt.Sprint(errorString)) + } + return err +} diff --git a/tests/v2/utils/library/test_utils.go b/tests/v2/utils/library/test_utils.go new file mode 100644 index 00000000..c5262ad5 --- /dev/null +++ b/tests/v2/utils/library/test_utils.go @@ -0,0 +1,337 @@ +package utils + +import ( + "errors" + "fmt" + "strconv" + "strings" + "testing" + "time" + + schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + header "github.com/devfile/api/v2/pkg/devfile" + devfilepkg "github.com/devfile/library/pkg/devfile" + "github.com/devfile/library/pkg/devfile/parser" + devfileCtx "github.com/devfile/library/pkg/devfile/parser/context" + devfileData "github.com/devfile/library/pkg/devfile/parser/data" + "github.com/devfile/library/pkg/devfile/parser/data/v2/common" + + commonUtils "github.com/devfile/api/v2/test/v200/utils/common" +) + +const ( + // numDevfiles : the number of devfiles to create for each test + numDevfiles = 5 + // numThreads : Number of threads used by multi-thread tests + numThreads = 5 +) + +// DevfileValidator struct for DevfileValidator interface. +// The DevfileValidator interface is test/v200/utils/common/test_utils.go of the devfile/api repository. +type DevfileValidator struct{} + +// WriteAndValidate implements DevfileValidator interface. +// writes to disk and validates the specified devfile +func (devfileValidator DevfileValidator) WriteAndValidate(devfile *commonUtils.TestDevfile) error { + err := writeDevfile(devfile) + if err != nil { + commonUtils.LogErrorMessage(fmt.Sprintf("Error writing file : %s : %v", devfile.FileName, err)) + } else { + err = validateDevfile(devfile) + if err != nil { + commonUtils.LogErrorMessage(fmt.Sprintf("Error vaidating file : %s : %v", devfile.FileName, err)) + } else { + err = verify(devfile) + } + } + return err +} + +// DevfileFollower struct for DevfileFollower interface. +// The DevfileFollower interface is defined in test/v200/utils/common/test_utils.go of the devfile/api repository +type DevfileFollower struct { + LibraryData devfileData.DevfileData +} + +// AddCommand adds the specified command to the library data +func (devfileFollower DevfileFollower) AddCommand(command schema.Command) error { + return devfileFollower.LibraryData.AddCommands(command) +} + +// UpdateCommand updates the specified command in the library data +func (devfileFollower DevfileFollower) UpdateCommand(command schema.Command) { + devfileFollower.LibraryData.UpdateCommand(command) +} + +// AddComponent adds the specified component to the library data +func (devfileFollower DevfileFollower) AddComponent(component schema.Component) error { + var components []schema.Component + components = append(components, component) + return devfileFollower.LibraryData.AddComponents(components) +} + +// UpdateComponent updates the specified component in the library data +func (devfileFollower DevfileFollower) UpdateComponent(component schema.Component) { + devfileFollower.LibraryData.UpdateComponent(component) +} + +// AddProject adds the specified project to the library data +func (devfileFollower DevfileFollower) AddProject(project schema.Project) error { + var projects []schema.Project + projects = append(projects, project) + return devfileFollower.LibraryData.AddProjects(projects) +} + +// UpdateProject updates the specified project in the library data +func (devfileFollower DevfileFollower) UpdateProject(project schema.Project) { + devfileFollower.LibraryData.UpdateProject(project) +} + +// AddStarterProject adds the specified starter project to the library data +func (devfileFollower DevfileFollower) AddStarterProject(starterProject schema.StarterProject) error { + var starterProjects []schema.StarterProject + starterProjects = append(starterProjects, starterProject) + return devfileFollower.LibraryData.AddStarterProjects(starterProjects) +} + +// UpdateStarterProject updates the specified starter project in the library data +func (devfileFollower DevfileFollower) UpdateStarterProject(starterProject schema.StarterProject) { + devfileFollower.LibraryData.UpdateStarterProject(starterProject) +} + +// AddEvent adds the specified event to the library data +func (devfileFollower DevfileFollower) AddEvent(event schema.Events) error { + return devfileFollower.LibraryData.AddEvents(event) +} + +// UpdateEvent updates the specified event in the library data +func (devfileFollower DevfileFollower) UpdateEvent(event schema.Events) { + devfileFollower.LibraryData.UpdateEvents(event.PreStart, event.PostStart, event.PreStop, event.PostStop) +} + +// SetParent sets the specified parent in the library data +func (devfileFollower DevfileFollower) SetParent(parent schema.Parent) error { + devfileFollower.LibraryData.SetParent(&parent) + return nil +} + +// UpdateParent updates the specified parent in the library data +func (devfileFollower DevfileFollower) UpdateParent(parent schema.Parent) { + devfileFollower.LibraryData.SetParent(&parent) +} + +// SetMetaData sets the specified metaData in the library data +func (devfileFollower DevfileFollower) SetMetaData(metaData header.DevfileMetadata) error { + devfileFollower.LibraryData.SetMetadata(metaData) + return nil +} + +// UpdateMetaData updates the specified UpdateMetaData in the library data +func (devfileFollower DevfileFollower) UpdateMetaData(updateMetaData header.DevfileMetadata) { + devfileFollower.LibraryData.SetMetadata(updateMetaData) +} + +// SetMetaData sets the specified schemaVersion in the library data +func (devfileFollower DevfileFollower) SetSchemaVersion(schemaVersion string) { + devfileFollower.LibraryData.SetSchemaVersion(schemaVersion) +} + +// WriteDevfile uses the library to create a devfile on disk for use in a test. +func writeDevfile(devfile *commonUtils.TestDevfile) error { + var err error + + fileName := devfile.FileName + if !strings.HasSuffix(fileName, ".yaml") { + fileName += ".yaml" + } + + commonUtils.LogInfoMessage(fmt.Sprintf("Use Parser to write devfile %s", fileName)) + + ctx := devfileCtx.NewDevfileCtx(fileName) + + err = ctx.SetAbsPath() + if err != nil { + commonUtils.LogErrorMessage(fmt.Sprintf("Setting devfile path : %v", err)) + } else { + devObj := parser.DevfileObj{ + Ctx: ctx, + Data: devfile.Follower.(DevfileFollower).LibraryData, + } + err = devObj.WriteYamlDevfile() + if err != nil { + commonUtils.LogErrorMessage(fmt.Sprintf("Writing devfile : %v", err)) + } + } + + return err +} + +// validateDevfile uses the library to parse and validate a devfile on disk +func validateDevfile(devfile *commonUtils.TestDevfile) error { + + var err error + + commonUtils.LogInfoMessage(fmt.Sprintf("Parse and Validate %s : ", devfile.FileName)) + libraryObj, err := devfilepkg.ParseAndValidate(devfile.FileName) + if err != nil { + commonUtils.LogErrorMessage(fmt.Sprintf("From ParseAndValidate %v : ", err)) + } else { + follower := devfile.Follower.(DevfileFollower) + follower.LibraryData = libraryObj.Data + } + + return err +} + +// RunMultiThreadTest : Runs the same test on multiple threads, the test is based on the content of the specified TestContent +func RunMultiThreadTest(testContent commonUtils.TestContent, t *testing.T) { + + commonUtils.LogMessage(fmt.Sprintf("Start Threaded test for %s", testContent.FileName)) + + devfileName := testContent.FileName + var i int + for i = 1; i < numThreads; i++ { + testContent.FileName = commonUtils.AddSuffixToFileName(devfileName, "T"+strconv.Itoa(i)+"-") + go RunTest(testContent, t) + } + testContent.FileName = commonUtils.AddSuffixToFileName(devfileName, "T"+strconv.Itoa(i)+"-") + RunTest(testContent, t) + + commonUtils.LogMessage(fmt.Sprintf("Sleep 3 seconds to allow all threads to complete : %s", devfileName)) + time.Sleep(3 * time.Second) + commonUtils.LogMessage(fmt.Sprintf("Sleep complete : %s", devfileName)) + +} + +// RunTest : Runs a test to create and verify a devfile based on the content of the specified TestContent +func RunTest(testContent commonUtils.TestContent, t *testing.T) { + + commonUtils.LogMessage(fmt.Sprintf("Start test for %s", testContent.FileName)) + + devfileName := testContent.FileName + for i := 1; i <= numDevfiles; i++ { + + testContent.FileName = commonUtils.AddSuffixToFileName(devfileName, strconv.Itoa(i)) + commonUtils.LogMessage(fmt.Sprintf("Start test for %s", testContent.FileName)) + + validator := DevfileValidator{} + follower := DevfileFollower{} + libraryData, err := devfileData.NewDevfileData("2.0.0") + if err != nil { + t.Fatalf(commonUtils.LogMessage(fmt.Sprintf("Error creating parser data : %v", err))) + } + libraryData.SetSchemaVersion("2.0.0") + follower.LibraryData = libraryData + commonUtils.LogMessage(fmt.Sprintf("Parser data created with schema version : %s", follower.LibraryData.GetSchemaVersion())) + + testDevfile, err := commonUtils.GetDevfile(testContent.FileName, follower, validator) + if err != nil { + t.Fatalf(commonUtils.LogMessage(fmt.Sprintf("Error creating devfile : %v", err))) + } + + testDevfile.RunTest(testContent, t) + + if testContent.EditContent { + if len(testContent.CommandTypes) > 0 { + err = editCommands(&testDevfile) + if err != nil { + t.Fatalf(commonUtils.LogErrorMessage(fmt.Sprintf("ERROR editing commands : %s : %v", testContent.FileName, err))) + } + } + if len(testContent.ComponentTypes) > 0 { + err = editComponents(&testDevfile) + if err != nil { + t.Fatalf(commonUtils.LogErrorMessage(fmt.Sprintf("ERROR editing components : %s : %v", testContent.FileName, err))) + } + } + + validator.WriteAndValidate(&testDevfile) + } + } +} + +// verify verifies the library contents of the specified devfile with the expected content +func verify(devfile *commonUtils.TestDevfile) error { + + commonUtils.LogInfoMessage(fmt.Sprintf("Verify %s : ", devfile.FileName)) + + var errorString []string + + libraryData := devfile.Follower.(DevfileFollower).LibraryData + commonUtils.LogInfoMessage(fmt.Sprintf("Get commands %s : ", devfile.FileName)) + commands, err := libraryData.GetCommands(common.DevfileOptions{}) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Getting Commands from library : %s : %v", devfile.FileName, err))) + } else { + if commands != nil && len(commands) > 0 { + err := VerifyCommands(devfile, commands) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Verfify Commands %s : %v", devfile.FileName, err))) + } + } else { + commonUtils.LogInfoMessage(fmt.Sprintf("No commands found in %s : ", devfile.FileName)) + } + } + + commonUtils.LogInfoMessage(fmt.Sprintf("Get components %s : ", devfile.FileName)) + components, err := libraryData.GetComponents(common.DevfileOptions{}) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Getting Components from library : %s : %v", devfile.FileName, err))) + } else { + if components != nil && len(components) > 0 { + err := VerifyComponents(devfile, components) + if err != nil { + errorString = append(errorString, commonUtils.LogErrorMessage(fmt.Sprintf("Verfify Components %s : %v", devfile.FileName, err))) + } + } else { + commonUtils.LogInfoMessage(fmt.Sprintf("No components found in %s : ", devfile.FileName)) + } + } + + var returnError error + if len(errorString) > 0 { + returnError = errors.New(fmt.Sprint(errorString)) + } + return returnError + +} + +// editCommands modifies random attributes for each of the commands in the devfile. +func editCommands(devfile *commonUtils.TestDevfile) error { + + commonUtils.LogInfoMessage(fmt.Sprintf("Edit %s : ", devfile.FileName)) + + commonUtils.LogInfoMessage(fmt.Sprintf(" -> Get commands %s : ", devfile.FileName)) + commands, err := devfile.Follower.(DevfileFollower).LibraryData.GetCommands(common.DevfileOptions{}) + if err != nil { + commonUtils.LogErrorMessage(fmt.Sprintf("Getting commands from library : %s : %v", devfile.FileName, err)) + } else { + for _, command := range commands { + err = UpdateCommand(devfile, command.Id) + if err != nil { + commonUtils.LogErrorMessage(fmt.Sprintf("Updating command : %s : %v", devfile.FileName, err)) + } + } + } + return err +} + +// editComponents modifies random attributes for each of the components in the devfile. +func editComponents(devfile *commonUtils.TestDevfile) error { + + commonUtils.LogInfoMessage(fmt.Sprintf("Edit %s : ", devfile.FileName)) + + commonUtils.LogInfoMessage(fmt.Sprintf(" -> Get components %s : ", devfile.FileName)) + components, err := devfile.Follower.(DevfileFollower).LibraryData.GetComponents(common.DevfileOptions{}) + if err != nil { + commonUtils.LogErrorMessage(fmt.Sprintf("Getting components from library : %s : %v", devfile.FileName, err)) + } else { + for _, component := range components { + err = UpdateComponent(devfile, component.Name) + if err != nil { + commonUtils.LogErrorMessage(fmt.Sprintf("Updating component : %s : %v", devfile.FileName, err)) + } + } + } + return err +} diff --git a/tests/v2/utils/test_utils.go b/tests/v2/utils/test_utils.go deleted file mode 100644 index bcd04654..00000000 --- a/tests/v2/utils/test_utils.go +++ /dev/null @@ -1,383 +0,0 @@ -package utils - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "math/rand" - "os" - "path/filepath" - "runtime" - "strings" - "time" - - schema "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - devfilepkg "github.com/devfile/library/pkg/devfile" - "github.com/devfile/library/pkg/devfile/parser" - devfileCtx "github.com/devfile/library/pkg/devfile/parser/context" - devfileData "github.com/devfile/library/pkg/devfile/parser/data" - "github.com/devfile/library/pkg/devfile/parser/data/v2/common" - "sigs.k8s.io/yaml" -) - -const ( - defaultTempDir = "./tmp/" - logFileName = "test.log" - // logToFileOnly - If set to false the log output will also be output to the console - logToFileOnly = true // If set to false the log output will also be output to the console -) - -// tmpDir temporary directory in use -var tmpDir string - -var ( - testLogger *log.Logger -) - -// init creates: -// - the temporary directory used by the test to store logs and generated devfiles. -// - the log file -func init() { - tmpDir = defaultTempDir - if _, err := os.Stat(tmpDir); !os.IsNotExist(err) { - os.RemoveAll(tmpDir) - } - if err := os.Mkdir(tmpDir, 0755); err != nil { - fmt.Printf("Failed to create temp directory, will use current directory : %v ", err) - tmpDir = "./" - } - f, err := os.OpenFile(filepath.Join(tmpDir, logFileName), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - fmt.Printf("Error creating Log file : %v", err) - } else { - if logToFileOnly { - testLogger = log.New(f, "", log.LstdFlags|log.Lmicroseconds) - } else { - writer := io.MultiWriter(f, os.Stdout) - testLogger = log.New(writer, "", log.LstdFlags|log.Lmicroseconds) - } - testLogger.Println("Test Starting:") - } - -} - -// CreateTempDir creates a specified sub directory under the temp directory if it does not exist. -// Returns the name of the created directory. -func CreateTempDir(subdir string) string { - tempDir := tmpDir + subdir + "/" - var err error - if _, err = os.Stat(tempDir); os.IsNotExist(err) { - err = os.Mkdir(tempDir, 0755) - } - if err != nil { - // if cannot create subdirectory just use the base tmp directory - LogErrorMessage(fmt.Sprintf("Failed to create temp directory %s will use %s : %v", tempDir, tmpDir, err)) - tempDir = tmpDir - } - return tempDir -} - -// GetDevFileName returns a qualified name of a devfile for use in a test. -// The devfile will be in a temporary directory and is named using the calling function's name. -func GetDevFileName() string { - pc, fn, _, ok := runtime.Caller(1) - if !ok { - return tmpDir + "DefaultDevfile" - } - - testFile := filepath.Base(fn) - testFileExtension := filepath.Ext(testFile) - subdir := testFile[0 : len(testFile)-len(testFileExtension)] - destDir := CreateTempDir(subdir) - callerName := runtime.FuncForPC(pc).Name() - pos1 := strings.LastIndex(callerName, "/parserTest.") + len("/parserTest.") - devfileName := destDir + callerName[pos1:len(callerName)] + ".yaml" - - LogInfoMessage(fmt.Sprintf("GetDevFileName : %s", devfileName)) - - return devfileName -} - -// AddSuffixToFileName adds a specified suffix to the name of a specified file. -// For example if the file is devfile.yaml and the suffix is 1, the result is devfile1.yaml -func AddSuffixToFileName(fileName string, suffix string) string { - pos1 := strings.LastIndex(fileName, ".yaml") - newFileName := fileName[0:pos1] + suffix + ".yaml" - LogInfoMessage(fmt.Sprintf("Add suffix %s to fileName %s : %s", suffix, fileName, newFileName)) - return newFileName -} - -// LogMessage logs the specified message and returns the message logged -func LogMessage(message string) string { - if testLogger != nil { - testLogger.Println(message) - } else { - fmt.Printf("Logger not available: %s", message) - } - return message -} - -var errorPrefix = "..... ERROR : " -var infoPrefix = "INFO :" - -// LogErrorMessage logs the specified message as an error message and returns the message logged -func LogErrorMessage(message string) string { - var errMessage []string - errMessage = append(errMessage, errorPrefix, message) - return LogMessage(fmt.Sprint(errMessage)) -} - -// LogInfoMessage logs the specified message as an info message and returns the message logged -func LogInfoMessage(message string) string { - var infoMessage []string - infoMessage = append(infoMessage, infoPrefix, message) - return LogMessage(fmt.Sprint(infoMessage)) -} - -// TestDevfile is a structure used to track a test devfile and its contents -type TestDevfile struct { - SchemaDevFile schema.Devfile - FileName string - ParserData devfileData.DevfileData - SchemaParsed bool - GroupDefaults map[schema.CommandGroupKind]bool - UsedPorts map[int]bool -} - -var StringCount int = 0 - -var RndSeed int64 = time.Now().UnixNano() - -// GetRandomUniqueString returns a unique random string which is n characters long plus an integer to ensure uniqueness -// If lower is set to true a lower case string is returned. -func GetRandomUniqueString(n int, lower bool) string { - StringCount++ - return fmt.Sprintf("%s%04d", GetRandomString(n, lower), StringCount) -} - -// Creates a unique seed for the randon generation. -func setRandSeed() { - RndSeed++ - rand.Seed(RndSeed) -} - -const schemaBytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - -// GetRandomString returns a random string which is n characters long. -// If lower is set to true a lower case string is returned. -func GetRandomString(n int, lower bool) string { - setRandSeed() - b := make([]byte, n) - for i := range b { - b[i] = schemaBytes[rand.Intn(len(schemaBytes)-1)] - } - randomString := string(b) - if lower { - randomString = strings.ToLower(randomString) - } - return randomString -} - -var GroupKinds = [...]schema.CommandGroupKind{schema.BuildCommandGroupKind, schema.RunCommandGroupKind, schema.TestCommandGroupKind, schema.DebugCommandGroupKind} - -// GetRandomGroupKind return random group kind. One of "build", "run", "test" or "debug" -func GetRandomGroupKind() schema.CommandGroupKind { - return GroupKinds[GetRandomNumber(len(GroupKinds))-1] -} - -// GetBinaryDecision randomly returns true or false -func GetBinaryDecision() bool { - return GetRandomDecision(1, 1) -} - -// GetRandomDecision randomly returns true or false, but weighted to one or the other. -// For example if success is set to 2 and failure to 1, true is twice as likely to be returned. -func GetRandomDecision(success int, failure int) bool { - setRandSeed() - return rand.Intn(success+failure) > failure-1 -} - -// GetRandomNumber randomly returns an integer between 1 and the number specified. -func GetRandomNumber(max int) int { - setRandSeed() - return rand.Intn(max) + 1 -} - -// GetDevfile returns a structure used to represent a specific devfile in a test -func GetDevfile(fileName string) (TestDevfile, error) { - - var err error - testDevfile := TestDevfile{} - testDevfile.SchemaDevFile = schema.Devfile{} - testDevfile.FileName = fileName - testDevfile.SchemaDevFile.SchemaVersion = "2.0.0" - testDevfile.SchemaParsed = false - testDevfile.GroupDefaults = make(map[schema.CommandGroupKind]bool) - for _, kind := range GroupKinds { - testDevfile.GroupDefaults[kind] = false - } - testDevfile.ParserData, err = devfileData.NewDevfileData(testDevfile.SchemaDevFile.SchemaVersion) - if err != nil { - return testDevfile, err - } - testDevfile.ParserData.SetSchemaVersion(testDevfile.SchemaDevFile.SchemaVersion) - testDevfile.UsedPorts = make(map[int]bool) - return testDevfile, err -} - -// WriteDevfile create a devifle on disk for use in a test. -// If useParser is true the parser library is used to generate the file, otherwise "sigs.k8s.io/yaml" is used. -func (devfile *TestDevfile) WriteDevfile(useParser bool) error { - var err error - - fileName := devfile.FileName - if !strings.HasSuffix(fileName, ".yaml") { - fileName += ".yaml" - } - - if useParser { - LogInfoMessage(fmt.Sprintf("Use Parser to write devfile %s", fileName)) - - ctx := devfileCtx.NewDevfileCtx(fileName) - - err = ctx.SetAbsPath() - if err != nil { - LogErrorMessage(fmt.Sprintf("Setting devfile path : %v", err)) - } else { - devObj := parser.DevfileObj{ - Ctx: ctx, - Data: devfile.ParserData, - } - err = devObj.WriteYamlDevfile() - if err != nil { - LogErrorMessage(fmt.Sprintf("Writing devfile : %v", err)) - } else { - devfile.SchemaParsed = false - } - } - - } else { - LogInfoMessage(fmt.Sprintf("Marshall and write devfile %s", devfile.FileName)) - c, marshallErr := yaml.Marshal(&(devfile.SchemaDevFile)) - - if marshallErr != nil { - err = errors.New(LogErrorMessage(fmt.Sprintf("Marshall devfile %s : %v", devfile.FileName, marshallErr))) - } else { - err = ioutil.WriteFile(fileName, c, 0644) - if err != nil { - LogErrorMessage(fmt.Sprintf("Write devfile %s : %v", devfile.FileName, err)) - } else { - devfile.SchemaParsed = false - } - } - } - return err -} - -// parseSchema uses the parser to parse a devfile on disk -func (devfile *TestDevfile) parseSchema() error { - - var err error - if !devfile.SchemaParsed { - err = devfile.WriteDevfile(true) - if err != nil { - LogErrorMessage(fmt.Sprintf("From WriteDevfile %v : ", err)) - } else { - LogInfoMessage(fmt.Sprintf("Parse and Validate %s : ", devfile.FileName)) - parsedSchemaObj, parse_err := devfilepkg.ParseAndValidate(devfile.FileName) - if parse_err != nil { - err = parse_err - LogErrorMessage(fmt.Sprintf("From ParseAndValidate %v : ", err)) - } - devfile.SchemaParsed = true - devfile.ParserData = parsedSchemaObj.Data - } - } - return err -} - -// Verify verifies the contents of the specified devfile with the expected content -func (devfile *TestDevfile) Verify() error { - - LogInfoMessage(fmt.Sprintf("Verify %s : ", devfile.FileName)) - - var errorString []string - - err := devfile.parseSchema() - - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("parsing schema %s : %v", devfile.FileName, err))) - } else { - LogInfoMessage(fmt.Sprintf("Get commands %s : ", devfile.FileName)) - commands, _ := devfile.ParserData.GetCommands(common.DevfileOptions{}) - if commands != nil && len(commands) > 0 { - err = devfile.VerifyCommands(commands) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("Verfify Commands %s : %v", devfile.FileName, err))) - } - } else { - LogInfoMessage(fmt.Sprintf("No command found in %s : ", devfile.FileName)) - } - - LogInfoMessage(fmt.Sprintf("Get components %s : ", devfile.FileName)) - components, _ := devfile.ParserData.GetComponents(common.DevfileOptions{}) - if components != nil && len(components) > 0 { - err = devfile.VerifyComponents(components) - if err != nil { - errorString = append(errorString, LogErrorMessage(fmt.Sprintf("Verfify Commands %s : %v", devfile.FileName, err))) - } - } else { - LogInfoMessage(fmt.Sprintf("No components found in %s : ", devfile.FileName)) - } - } - var returnError error - if len(errorString) > 0 { - returnError = errors.New(fmt.Sprint(errorString)) - } - return returnError - -} - -// EditCommands modifies random attributes for each of the commands in the devfile. -func (devfile *TestDevfile) EditCommands() error { - - LogInfoMessage(fmt.Sprintf("Edit %s : ", devfile.FileName)) - - err := devfile.parseSchema() - if err != nil { - LogErrorMessage(fmt.Sprintf("From parser : %v", err)) - } else { - LogInfoMessage(fmt.Sprintf(" -> Get commands %s : ", devfile.FileName)) - commands, _ := devfile.ParserData.GetCommands(common.DevfileOptions{}) - for _, command := range commands { - err = devfile.UpdateCommand(command.Id) - if err != nil { - LogErrorMessage(fmt.Sprintf("Updating command : %v", err)) - } - } - } - return err -} - -// EditComponents modifies random attributes for each of the components in the devfile. -func (devfile *TestDevfile) EditComponents() error { - - LogInfoMessage(fmt.Sprintf("Edit %s : ", devfile.FileName)) - - err := devfile.parseSchema() - if err != nil { - LogErrorMessage(fmt.Sprintf("From parser : %v", err)) - } else { - LogInfoMessage(fmt.Sprintf(" -> Get commands %s : ", devfile.FileName)) - components, _ := devfile.ParserData.GetComponents(common.DevfileOptions{}) - for _, component := range components { - err = devfile.UpdateComponent(component.Name) - if err != nil { - LogErrorMessage(fmt.Sprintf("Updating component : %v", err)) - } - } - } - return err -}