diff --git a/go.mod b/go.mod index 4761fdb9..ca472d61 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/fatih/color v1.7.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/gobwas/glob v0.2.3 + github.com/google/go-cmp v0.4.0 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 github.com/mattn/go-colorable v0.1.2 // indirect diff --git a/tests/src/tests/README.md b/tests/src/tests/README.md new file mode 100644 index 00000000..e40eac6f --- /dev/null +++ b/tests/src/tests/README.md @@ -0,0 +1,260 @@ +# Devfile Parser Library Tests + +## About + +The tests use the go language and are intended to test every apsect of the parser for every schema attribute. Some basic aspects of the tests: + +* A first test (parser_v200_schema_test.go) feeds pre-created devfiles to the parser to ensure the parser can parse all attribues and return an approproate error when the devfile contains an error. +* A second set of tests (parser_v200_verify_test.go) create devfile content at runtime: + * Devfile content is randomly generated and as a result the tests are designed to run multiple times. + * Parser functions covered: + * Read an existing devfile. + * Write a new devfile. + * Modify Content of a devfile. + * Multi-threaded access to the parser. + * The tests use the devfile schema to create a structure containing expected content for a devfile. These structures are compared with those returned by the parser. + * sigs.k8s.io/yaml is used to write out devfiles. + * github.com/google/go-cmp/cmp is used to compare structures. + +## Current tests: + +The tests using pre-created devfiles are complete (but update in progress due to schema changes) + +The tests which generate devfiles with random content at run time currently cover the following properties and items. + +* Commands: + * Exec + * Composite +* Components + * Container + * Volume + +## Running the tests + +1. Go to directory tests/src/tests +1. Run ```go test``` or ```go test -v``` +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. + +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 + CreateWithParser bool + 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.CreateWithParser = true + 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. +* A helper structure is used to track each created object, these are kept in a maps. +* 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 pasrer + * the parser is used to write the devfile to disk + * the parser is then used to read and validate the modified devfile. +* Each object in then devfile is then retrieved from the parser and checked + * The object returned by the parser is compared to the equivalent object tracked in the test. + * if the obejcst do not match the test fails + * 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 createApplyCommand() *schema.ApplyCommand``` + * creates the apply command object and calls setApplyCommandValues to add attribute values + * ```func setApplyCommandValues(applyCommand *schema.ApplyCommand)``` + * randomly set attribute values in the provided apply command object + * follow the implementation of other similar functions. + * modify: + * ```type GenericCommand struct``` + * add a variabale to store the address of a schema.ApplyCommand object + * the value is set by the test for later comparision with that returned by the parser. + * one of these structures is created for each command created. + * ```func generateCommand(command *schema.Command, genericCommand *GenericCommand)``` + * add logic to call createApplyCommand if commandType indicates such. + * store the generated ApplyCommand object in GenericCommand structure. + * ```func (devfile *TestDevfile) UpdateCommand(command *schema.Command) error``` + * add logic to call setApplyCommandValues if commandType indicates such. + * update the ApplyCommand object in GenericCommand structure. + * ```func (devfile TestDevfile) VerifyCommands(commands map[string]schema.Command)``` + * add logic to compare an apply command object returned by the parser with that stored in the GenericCommand structure +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 logic to ```runTest(testContent TestContent, t *testing.T)``` for creatin and editing the + +### 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 + * Appy (to be implemented) + * Composite + * VSCodeLaunch (to be implemented) + * VSCodeTask (to be implemented) + * Each of these command-types have equivalent functions: + * ```func createCommand() *schema.``` + * creates the command object and calls ```setCommandValues``` to add attribute values + * for example see: ```func createExecCommand(execCommand *schema.ExecCommand)``` + * ```func setCommandValues(project-sourceProject *schema.)``` + * sets random attributes into the provided object + * for example see: ```func setExecCommandValues(execCommand *schema.ExecCommand)``` + * GenericCommand structure is created, one per command to track its content, for example: + ``` + type GenericCommand struct { + Name string + Verified bool + CommandType schema.CommandType + ExecCommandSchema *schema.ExecCommand + SchemaCompositeCommand *schema.CompositeCommand + } + ``` + + * Functions general to all commands + * ```func (devfile *TestDevfile) addCommand(commandType schema.CommandType) string``` + * main entry point for a test to add a command + * maintains the array of commands in the schema structure + * creates a GenericCommand object to store information about the created command + * calls generateCommand() + * saves the GenericCommand object in a map for access later + * ```func generateCommand(command *schema.Command, genericCommand *GenericCommand)``` + * includes logic to call the ```createCommand``` function for the command-Type of the supplied command object. + * stores a refrence to the generated command object in GenericCommand structure. + * ```func (devfile *TestDevfile) UpdateCommand(command *schema.Command) error``` + * includes logic to call setCommandValues for each commandType. + * update the equiavalent object in GenericCommand structure. + * ```func (devfile TestDevfile) VerifyCommands(commands map[string]schema.Command)``` + * add logic to compare an each command object returned by the parser with that stored in the GenericCommand structure. +1. ```test-utils.go``` + * Includes code for all properties + * Support for keeping a map of command objects + * Command map is in ```type TestDevfile struct``` + * usees command id as the key. + * functions to add and get from the map + * ```func (devfile *TestDevfile) MapCommand(command GenericCommand)``` + * ```func (devfile *TestDevfile) GetCommand(id string) *GenericCommand``` + * ```func (devfile *TestDevfile) CreateDevfile(useParser bool)``` + * Includes code when using the parser to create the devfile + * for commands: code required to add command objects to the the parser. + * ```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)``` includes logic + 1. Add commands to the test + 2. Starts 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.GenerateCommand + 1. command-test-utils.createExecCommand + 1. command-test-utils.setExecCommandValues + 1. test-utils.MapCommand + 1. test-utils.CreateDevfile + 1. test-utils.EditCommands + 1. command-test-utils.UpdateCommand + 1. command-test-utils.setExecCommandValues + 1. test-utils.Verify + 1. command-test-utils.VerifyCommands + + + + + + diff --git a/tests/src/tests/command_test_utils.go b/tests/src/tests/command_test_utils.go new file mode 100644 index 00000000..9d99a78d --- /dev/null +++ b/tests/src/tests/command_test_utils.go @@ -0,0 +1,262 @@ +package tests + +import ( + "errors" + "fmt" + + "github.com/google/go-cmp/cmp" + + schema "github.com/devfile/api/pkg/apis/workspaces/v1alpha2" +) + +type GenericCommand struct { + Id string + Verified bool + CommandType schema.CommandType + ExecCommandSchema *schema.ExecCommand + SchemaCompositeCommand *schema.CompositeCommand +} + +func (genericCommand *GenericCommand) setVerified() { + genericCommand.Verified = true +} + +func (genericCommand *GenericCommand) setId(id string) { + genericCommand.Id = id +} + +func (genericCommand *GenericCommand) checkId(command schema.Command) bool { + return genericCommand.Id == command.Id +} + +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) + LogMessage(fmt.Sprintf(" ....... Add Env: %s", commandEnvs[i])) + } + return commandEnvs +} + +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) + LogMessage(fmt.Sprintf(" ....... Add attribute : %s = %s", AttributeName, attributes[AttributeName])) + } + return attributes +} + +func addGroup() *schema.CommandGroup { + + commandGroup := schema.CommandGroup{} + + commandGroup.Kind = GetRandomGroupKind() + LogMessage(fmt.Sprintf(" ....... group Kind: %s", commandGroup.Kind)) + commandGroup.IsDefault = GetBinaryDecision() + LogMessage(fmt.Sprintf(" ....... group isDefault: %t", commandGroup.IsDefault)) + + return &commandGroup +} + +func (devfile *TestDevfile) addCommand(commandType schema.CommandType) string { + + var commands []schema.Command + index := 0 + if devfile.SchemaDevFile.Commands != nil { + // Commands already exist so expand the current slice + currentSize := len(devfile.SchemaDevFile.Commands) + commands = make([]schema.Command, currentSize+1) + for _, command := range devfile.SchemaDevFile.Commands { + commands[index] = command + index++ + } + } else { + commands = make([]schema.Command, 1) + } + + genericCommand := GenericCommand{} + genericCommand.CommandType = commandType + + generateCommand(&commands[index], &genericCommand) + + devfile.MapCommand(genericCommand) + devfile.SchemaDevFile.Commands = commands + + return commands[index].Id + +} + +func generateCommand(command *schema.Command, genericCommand *GenericCommand) { + + command.Id = GetRandomUniqueString(8, true) + genericCommand.Id = command.Id + LogMessage(fmt.Sprintf(" ....... id: %s", command.Id)) + + if genericCommand.CommandType == schema.ExecCommandType { + command.Exec = createExecCommand() + genericCommand.ExecCommandSchema = command.Exec + } else if genericCommand.CommandType == schema.CompositeCommandType { + command.Composite = createCompositeCommand() + genericCommand.SchemaCompositeCommand = command.Composite + } +} + +func (devfile *TestDevfile) UpdateCommand(command *schema.Command) error { + + errorString := "" + genericCommand := devfile.GetCommand(command.Id) + if genericCommand != nil { + LogMessage(fmt.Sprintf(" ....... Updating command id: %s", command.Id)) + if genericCommand.CommandType == schema.ExecCommandType { + setExecCommandValues(command.Exec) + genericCommand.ExecCommandSchema = command.Exec + } else if genericCommand.CommandType == schema.CompositeCommandType { + setCompositeCommandValues(command.Composite) + genericCommand.SchemaCompositeCommand = command.Composite + } + } else { + errorString += LogMessage(fmt.Sprintf(" ....... Command not found in test : %s", command.Id)) + } + var err error + if errorString != "" { + err = errors.New(errorString) + } + return err +} + +func createExecCommand() *schema.ExecCommand { + + LogMessage("Create a composite command :") + + execCommand := schema.ExecCommand{} + + setExecCommandValues(&execCommand) + + return &execCommand + +} + +func setExecCommandValues(execCommand *schema.ExecCommand) { + + execCommand.Component = GetRandomString(8, false) + LogMessage(fmt.Sprintf(" ....... component: %s", execCommand.Component)) + + execCommand.CommandLine = GetRandomString(4, false) + " " + GetRandomString(4, false) + LogMessage(fmt.Sprintf(" ....... commandLine: %s", execCommand.CommandLine)) + + if GetRandomDecision(2, 1) { + execCommand.Group = addGroup() + } else { + execCommand.Group = nil + } + + if GetBinaryDecision() { + execCommand.Label = GetRandomString(12, false) + LogMessage(fmt.Sprintf(" ....... label: %s", execCommand.Label)) + } else { + execCommand.Label = "" + } + + if GetBinaryDecision() { + execCommand.WorkingDir = "./tmp" + LogMessage(fmt.Sprintf(" ....... WorkingDir: %s", execCommand.WorkingDir)) + } else { + execCommand.WorkingDir = "" + } + + execCommand.HotReloadCapable = GetBinaryDecision() + LogMessage(fmt.Sprintf(" ....... HotReloadCapable: %t", execCommand.HotReloadCapable)) + + if GetBinaryDecision() { + execCommand.Env = AddEnv(GetRandomNumber(4)) + } else { + execCommand.Env = nil + } + +} + +func createCompositeCommand() *schema.CompositeCommand { + + compositeCommand := schema.CompositeCommand{} + + setCompositeCommandValues(&compositeCommand) + + return &compositeCommand +} + +func setCompositeCommandValues(compositeCommand *schema.CompositeCommand) { + numCommands := GetRandomNumber(3) + + compositeCommand.Commands = make([]string, numCommands) + for i := 0; i < numCommands; i++ { + compositeCommand.Commands[i] = GetRandomUniqueString(8, false) + LogMessage(fmt.Sprintf(" ....... command %d of %d : %s", i, numCommands, compositeCommand.Commands[i])) + } + + if GetRandomDecision(2, 1) { + compositeCommand.Group = addGroup() + } + + if GetBinaryDecision() { + compositeCommand.Label = GetRandomString(12, false) + LogMessage(fmt.Sprintf(" ....... label: %s", compositeCommand.Label)) + } + + if GetBinaryDecision() { + compositeCommand.Parallel = true + LogMessage(fmt.Sprintf(" ....... Parallel: %t", compositeCommand.Parallel)) + } +} + +func (devfile TestDevfile) VerifyCommands(commands []schema.Command) error { + + LogMessage("Enter VerifyCommands") + errorString := "" + + if devfile.CommandMap != nil { + for _, command := range commands { + + if matchedCommand, found := devfile.CommandMap[command.Id]; found { + matchedCommand.setVerified() + if matchedCommand.CommandType == schema.ExecCommandType { + if !cmp.Equal(*command.Exec, *matchedCommand.ExecCommandSchema) { + errorString += LogMessage(fmt.Sprintf(" ---> ERROR: Exec Command %s from parser: %v", command.Id, *command.Exec)) + errorString += LogMessage(fmt.Sprintf(" ---> ERROR: Exec Command %s from tester: %v", matchedCommand.Id, matchedCommand.ExecCommandSchema)) + } else { + LogMessage(fmt.Sprintf(" --> Exec command structures matched - id : %s ", command.Id)) + } + } + if matchedCommand.CommandType == schema.CompositeCommandType { + if !cmp.Equal(*command.Composite, *matchedCommand.SchemaCompositeCommand) { + errorString += LogMessage(fmt.Sprintf(" ---> ERROR: Composite Command from parser: %v", *command.Composite)) + errorString += LogMessage(fmt.Sprintf(" ---> ERROR: Composite Command from tester: %v", matchedCommand.SchemaCompositeCommand)) + } else { + LogMessage(fmt.Sprintf(" --> Composite command structures matched - id : %s ", command.Id)) + } + } + + } else { + errorString += LogMessage(fmt.Sprintf(" --> ERROR: Command from parser not known to test - id : %s ", command.Id)) + } + } + + for _, genericCommand := range devfile.CommandMap { + if !genericCommand.Verified { + errorString += LogMessage(fmt.Sprintf(" --> ERROR: Command not returned by parser - id : %s", genericCommand.Id)) + } + } + + } else { + if commands != nil { + errorString += LogMessage(" --> ERROR: Parser returned commands but Test does not include any.") + } + } + var err error + if errorString != "" { + err = errors.New(errorString) + } + return err +} diff --git a/tests/src/tests/component_test_utils.go b/tests/src/tests/component_test_utils.go new file mode 100644 index 00000000..a410972d --- /dev/null +++ b/tests/src/tests/component_test_utils.go @@ -0,0 +1,247 @@ +package tests + +import ( + "errors" + "fmt" + "strconv" + + schema "github.com/devfile/api/pkg/apis/workspaces/v1alpha2" + "github.com/google/go-cmp/cmp" +) + +type GenericComponent struct { + Name string + Verified bool + ComponentType schema.ComponentType + ContainerComponentSchema *schema.ContainerComponent + VolumeComponentSchema *schema.VolumeComponent +} + +func (genericComponent *GenericComponent) setVerified() { + genericComponent.Verified = true +} + +func (genericComponent *GenericComponent) setName(name string) { + genericComponent.Name = name +} + +func (genericComponent *GenericComponent) checkId(coomponent schema.Component) bool { + return genericComponent.Name == coomponent.Name +} + +func AddVolume(numVols int) []schema.VolumeMount { + commandVols := make([]schema.VolumeMount, numVols) + for i := 0; i < numVols; i++ { + commandVols[i].Name = "Name_" + GetRandomString(5, false) + commandVols[i].Path = "/Path_" + GetRandomString(5, false) + LogMessage(fmt.Sprintf(" ....... Add Volume: %s", commandVols[i])) + } + return commandVols +} + +func (devfile *TestDevfile) AddComponent(componentType schema.ComponentType) string { + + var components []schema.Component + index := 0 + if devfile.SchemaDevFile.Components != nil { + // Commands already exist so expand the current slice + currentSize := len(devfile.SchemaDevFile.Components) + components = make([]schema.Component, currentSize+1) + for _, component := range devfile.SchemaDevFile.Components { + components[index] = component + index++ + } + } else { + components = make([]schema.Component, 1) + } + + genericComponent := GenericComponent{} + genericComponent.ComponentType = componentType + + generateComponent(&components[index], &genericComponent) + + devfile.MapComponent(genericComponent) + devfile.SchemaDevFile.Components = components + + return components[index].Name + +} + +func generateComponent(component *schema.Component, genericComponent *GenericComponent) { + + component.Name = GetRandomUniqueString(8, true) + genericComponent.setName(component.Name) + LogMessage(fmt.Sprintf(" ....... Name: %s", component.Name)) + + if genericComponent.ComponentType == schema.ContainerComponentType { + component.Container = createContainerComponent() + genericComponent.ContainerComponentSchema = component.Container + } else if genericComponent.ComponentType == schema.VolumeComponentType { + component.Volume = createVolumeComponent() + genericComponent.VolumeComponentSchema = component.Volume + } +} + +func createContainerComponent() *schema.ContainerComponent { + + LogMessage("Create a container component :") + + containerComponent := schema.ContainerComponent{} + setContainerComponentValues(&containerComponent) + + return &containerComponent + +} + +func createVolumeComponent() *schema.VolumeComponent { + + LogMessage("Create a volume component :") + + volumeComponent := schema.VolumeComponent{} + setVolumeComponentValues(&volumeComponent) + + return &volumeComponent + +} + +func setContainerComponentValues(containerComponent *schema.ContainerComponent) { + + 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) + LogMessage(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) + LogMessage(fmt.Sprintf(" ....... arg %d of %d : %s", i, numArgs, containerComponent.Args[i])) + } + } + + containerComponent.DedicatedPod = GetBinaryDecision() + LogMessage(fmt.Sprintf(" ....... DedicatedPod: %t", containerComponent.DedicatedPod)) + + if GetBinaryDecision() { + containerComponent.MemoryLimit = strconv.Itoa(4+GetRandomNumber(124)) + "M" + LogMessage(fmt.Sprintf(" ....... MemoryLimit: %s", containerComponent.MemoryLimit)) + } + + if GetBinaryDecision() { + setMountSources := GetBinaryDecision() + containerComponent.MountSources = &setMountSources + LogMessage(fmt.Sprintf(" ....... MountSources: %t", *containerComponent.MountSources)) + + if setMountSources { + containerComponent.SourceMapping = "/" + GetRandomString(8, false) + LogMessage(fmt.Sprintf(" ....... SourceMapping: %s", containerComponent.SourceMapping)) + } + } + + if GetBinaryDecision() { + containerComponent.Env = AddEnv(GetRandomNumber(4)) + } else { + containerComponent.Env = nil + } + + if GetBinaryDecision() { + containerComponent.VolumeMounts = AddVolume(GetRandomNumber(4)) + } else { + containerComponent.Env = nil + } + + if GetBinaryDecision() { + containerComponent.Endpoints = CreateEndpoints() + } + +} + +func setVolumeComponentValues(volumeComponent *schema.VolumeComponent) { + + if GetRandomDecision(5, 1) { + volumeComponent.Size = strconv.Itoa(4+GetRandomNumber(252)) + "G" + LogMessage(fmt.Sprintf(" ....... volumeComponent.Size: %s", volumeComponent.Size)) + } + +} + +func (devfile *TestDevfile) UpdateComponent(component *schema.Component) error { + + errorString := "" + genericComponent := devfile.GetComponent(component.Name) + if genericComponent != nil { + LogMessage(fmt.Sprintf(" ....... Updating component name: %s", component.Name)) + if genericComponent.ComponentType == schema.ContainerComponentType { + setContainerComponentValues(component.Container) + genericComponent.ContainerComponentSchema = component.Container + } else if genericComponent.ComponentType == schema.VolumeComponentType { + setVolumeComponentValues(component.Volume) + genericComponent.VolumeComponentSchema = component.Volume + } + } else { + errorString += LogMessage(fmt.Sprintf(" ....... Component not found in test : %s", component.Name)) + } + var err error + if errorString != "" { + err = errors.New(errorString) + } + return err +} + +func (devfile TestDevfile) VerifyComponents(components []schema.Component) error { + + LogMessage("Enter VerifyComponents") + errorString := "" + + if devfile.ComponentMap != nil { + for _, component := range components { + + LogMessage(fmt.Sprintf(" --> Volume Component structures matched - name : %s ", component.Name)) + if matchedComponent, found := devfile.ComponentMap[component.Name]; found { + matchedComponent.setVerified() + if matchedComponent.ComponentType == schema.ContainerComponentType { + if !cmp.Equal(*component.Container, *matchedComponent.ContainerComponentSchema) { + errorString += LogMessage(fmt.Sprintf(" ---> ERROR: Container Component %s from parser: %v", component.Name, *component.Container)) + errorString += LogMessage(fmt.Sprintf(" ---> ERROR: Container Component %s from tester: %v", matchedComponent.Name, matchedComponent.ContainerComponentSchema)) + } else { + LogMessage(fmt.Sprintf(" --> Container Component structures matched - name : %s ", component.Name)) + } + } + if matchedComponent.ComponentType == schema.VolumeComponentType { + if !cmp.Equal(*component.Volume, *matchedComponent.VolumeComponentSchema) { + errorString += LogMessage(fmt.Sprintf(" ---> ERROR: Volume Component %s from parser: %v", component.Name, *component.Volume)) + errorString += LogMessage(fmt.Sprintf(" ---> ERROR: Volume Component %s from tester: %v", matchedComponent.Name, matchedComponent.VolumeComponentSchema)) + } else { + LogMessage(fmt.Sprintf(" --> Volume Component structures matched - name : %s ", component.Name)) + } + } + + } else { + errorString += LogMessage(fmt.Sprintf(" --> ERROR: Component from parser not known to test - - name : %s ", component.Name)) + } + } + + for _, genericComponent := range devfile.ComponentMap { + if !genericComponent.Verified { + errorString += LogMessage(fmt.Sprintf(" --> ERROR: Component not returned by parser - name : %s", genericComponent.Name)) + } + } + + } else { + if components != nil { + errorString += LogMessage(" --> ERROR: Parser returned components but Test does not include any.") + } + } + var err error + if errorString != "" { + err = errors.New(errorString) + } + return err +} diff --git a/tests/src/tests/endpoint-test-utils.go b/tests/src/tests/endpoint-test-utils.go new file mode 100644 index 00000000..ad5f62a7 --- /dev/null +++ b/tests/src/tests/endpoint-test-utils.go @@ -0,0 +1,59 @@ +package tests + +import ( + "fmt" + + schema "github.com/devfile/api/pkg/apis/workspaces/v1alpha2" +) + +var Exposures = [...]schema.EndpointExposure{schema.PublicEndpointExposure, schema.InternalEndpointExposure, schema.NoneEndpointExposure} + +func GetRandomExposure() schema.EndpointExposure { + return Exposures[GetRandomNumber(len(Exposures))-1] +} + +var Protocols = [...]schema.EndpointProtocol{schema.HTTPEndpointProtocol, schema.WSEndpointProtocol, schema.TCPEndpointProtocol, schema.UDPEndpointProtocol} + +func GetRandomProtocol() schema.EndpointProtocol { + return Protocols[GetRandomNumber(len(Protocols))-1] +} + +func CreateEndpoints() []schema.Endpoint { + + numEndpoints := GetRandomNumber(5) + endpoints := make([]schema.Endpoint, numEndpoints) + + for i := 0; i < numEndpoints; i++ { + + endpoint := schema.Endpoint{} + + endpoint.Name = GetRandomString(GetRandomNumber(15)+5, false) + LogMessage(fmt.Sprintf(" ....... add endpoint %d name : %s", i, endpoint.Name)) + + endpoint.TargetPort = GetRandomNumber(9999) + LogMessage(fmt.Sprintf(" ....... add endpoint %d targetPort: %d", i, endpoint.TargetPort)) + + if GetBinaryDecision() { + endpoint.Exposure = GetRandomExposure() + LogMessage(fmt.Sprintf(" ....... add endpoint %d exposure: %s", i, endpoint.Exposure)) + } + + if GetBinaryDecision() { + endpoint.Protocol = GetRandomProtocol() + LogMessage(fmt.Sprintf(" ....... add endpoint %d protocol: %s", i, endpoint.Protocol)) + } + + endpoint.Secure = GetBinaryDecision() + LogMessage(fmt.Sprintf(" ....... add endpoint %d secure: %t", i, endpoint.Secure)) + + if GetBinaryDecision() { + endpoint.Path = "/Path_" + GetRandomString(GetRandomNumber(10)+3, false) + LogMessage(fmt.Sprintf(" ....... add endpoint %d path: %s", i, endpoint.Path)) + } + + endpoints[i] = endpoint + + } + + return endpoints +} diff --git a/tests/src/tests/parser_v200_verify_test.go b/tests/src/tests/parser_v200_verify_test.go new file mode 100644 index 00000000..61bb3c41 --- /dev/null +++ b/tests/src/tests/parser_v200_verify_test.go @@ -0,0 +1,278 @@ +package tests + +import ( + "fmt" + "strconv" + "testing" + "time" + + schema "github.com/devfile/api/pkg/apis/workspaces/v1alpha2" +) + +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 + +type TestContent struct { + CommandTypes []schema.CommandType + ComponentTypes []schema.ComponentType + FileName string + CreateWithParser bool + EditContent bool +} + +func Test_ExecCommand(t *testing.T) { + testContent := TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType} + testContent.CreateWithParser = false + testContent.EditContent = false + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} +func Test_ExecCommandEdit(t *testing.T) { + testContent := TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType} + testContent.CreateWithParser = false + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_ExecCommandParserCreate(t *testing.T) { + testContent := TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType} + testContent.CreateWithParser = true + testContent.EditContent = false + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_ExecCommandEditParserCreate(t *testing.T) { + testContent := TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType} + testContent.CreateWithParser = true + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_CompositeCommand(t *testing.T) { + testContent := TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.CompositeCommandType} + testContent.CreateWithParser = false + testContent.EditContent = false + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} +func Test_CompositeCommandEdit(t *testing.T) { + testContent := TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.CompositeCommandType} + testContent.CreateWithParser = false + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_CompositeCommandParserCreate(t *testing.T) { + testContent := TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.CompositeCommandType} + testContent.CreateWithParser = true + testContent.EditContent = false + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_CompositeCommandEditParserCreate(t *testing.T) { + testContent := TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.CompositeCommandType} + testContent.CreateWithParser = true + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_MultiCommand(t *testing.T) { + testContent := TestContent{} + testContent.CommandTypes = []schema.CommandType{schema.ExecCommandType, schema.CompositeCommandType} + testContent.CreateWithParser = true + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_ContainerComponent(t *testing.T) { + testContent := TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType} + testContent.CreateWithParser = false + testContent.EditContent = false + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_ContainerComponentEdit(t *testing.T) { + testContent := TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType} + testContent.CreateWithParser = false + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_ContainerComponentCreateWithParser(t *testing.T) { + testContent := TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType} + testContent.CreateWithParser = true + testContent.EditContent = false + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_ContainerComponentEditCreateWithParser(t *testing.T) { + testContent := TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType} + testContent.CreateWithParser = true + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_VolumeComponent(t *testing.T) { + testContent := TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.VolumeComponentType} + testContent.CreateWithParser = false + testContent.EditContent = false + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_VolumeComponentEdit(t *testing.T) { + testContent := TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.VolumeComponentType} + testContent.CreateWithParser = false + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_VolumeComponentCreateWithParser(t *testing.T) { + testContent := TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.VolumeComponentType} + testContent.CreateWithParser = true + testContent.EditContent = false + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_VolumeComponentEditCreateWithParser(t *testing.T) { + testContent := TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.VolumeComponentType} + testContent.CreateWithParser = true + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func Test_MultiComponent(t *testing.T) { + testContent := TestContent{} + testContent.ComponentTypes = []schema.ComponentType{schema.ContainerComponentType, schema.VolumeComponentType} + testContent.CreateWithParser = true + testContent.EditContent = true + testContent.FileName = 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.CreateWithParser = true + testContent.EditContent = true + testContent.FileName = GetDevFileName() + runTest(testContent, t) + runMultiThreadTest(testContent, t) +} + +func runMultiThreadTest(testContent TestContent, t *testing.T) { + + LogMessage(fmt.Sprintf("Start Threaded test for %s", testContent.FileName)) + + devfileName := testContent.FileName + var i int + for i = 1; i < numThreads; i++ { + testContent.FileName = devfileName + strconv.Itoa(i) + go runTest(testContent, t) + } + testContent.FileName = devfileName + strconv.Itoa(i) + runTest(testContent, t) + + LogMessage(fmt.Sprintf("Sleep 2 seconds to allow all threads to complete : %s", devfileName)) + time.Sleep(2 * time.Second) + LogMessage(fmt.Sprintf("Sleep complete : %s", devfileName)) + +} + +func runTest(testContent TestContent, t *testing.T) { + + LogMessage(fmt.Sprintf("Start test for %s", testContent.FileName)) + testDevfile := GetDevfile(testContent.FileName) + + if len(testContent.CommandTypes) > 0 { + numCommands := GetRandomNumber(maxCommands) + for i := 0; i < numCommands; i++ { + commandIndex := GetRandomNumber(len(testContent.CommandTypes)) + testDevfile.addCommand(testContent.CommandTypes[commandIndex-1]) + } + } + + if len(testContent.ComponentTypes) > 0 { + numComponents := GetRandomNumber(maxComponents) + for i := 0; i < numComponents; i++ { + componentIndex := GetRandomNumber(len(testContent.ComponentTypes)) + testDevfile.AddComponent(testContent.ComponentTypes[componentIndex-1]) + } + } + + err := testDevfile.CreateDevfile(testContent.CreateWithParser) + if err != nil { + t.Fatalf(LogMessage(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(LogMessage(fmt.Sprintf("ERROR editing commands : %s : %v", testContent.FileName, err))) + } + } + if len(testContent.ComponentTypes) > 0 { + err = testDevfile.EditComponents() + if err != nil { + t.Fatalf(LogMessage(fmt.Sprintf("ERROR editing components : %s : %v", testContent.FileName, err))) + } + } + } + + err = testDevfile.Verify() + if err != nil { + t.Fatalf(LogMessage(fmt.Sprintf("ERROR verifying devfile content : %s : %v", testContent.FileName, err))) + } + +} diff --git a/tests/src/tests/test_utils.go b/tests/src/tests/test_utils.go new file mode 100644 index 00000000..5a39bc29 --- /dev/null +++ b/tests/src/tests/test_utils.go @@ -0,0 +1,341 @@ +package tests + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "math/rand" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + schema "github.com/devfile/api/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 tmpDir = "./tmp/" +const logErrorOnly = false +const logFileName = "test.log" +const logToFileOnly = true // If set to false the log output will also be output to the console + +var ( + testLogger *log.Logger +) + +func init() { + if _, err := os.Stat(tmpDir); !os.IsNotExist(err) { + os.RemoveAll(tmpDir) + } + os.Mkdir(tmpDir, 0755) + + 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:") + } +} + +func GetTempDir() string { + _, fn, _, ok := runtime.Caller(1) + if !ok { + return tmpDir + } + testFile := filepath.Base(fn) + testFileExtension := filepath.Ext(testFile) + subdir := testFile[0 : len(testFile)-len(testFileExtension)] + return CreateTempDir(subdir) +} + +func CreateTempDir(subdir string) string { + tempDir := tmpDir + subdir + "/" + if _, err := os.Stat(tempDir); os.IsNotExist(err) { + os.Mkdir(tempDir, 0755) + } + return tempDir +} + +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, "/tests.") + len("/tests.") + devfileName := callerName[pos1:len(callerName)] + + LogMessage(fmt.Sprintf("GetDevFileName : %s", destDir+devfileName)) + + return destDir + devfileName +} + +func LogMessage(message string) string { + testLogger.Println(message) + return message +} + +type TestDevfile struct { + SchemaDevFile schema.Devfile + FileName string + ParsedSchemaObj parser.DevfileObj + SchemaParsed bool + CommandMap map[string]*GenericCommand + ComponentMap map[string]*GenericComponent +} + +var StringCount int = 0 + +var RndSeed int64 = time.Now().UnixNano() + +func GetRandomUniqueString(n int, lower bool) string { + StringCount++ + return fmt.Sprintf("%s%04d", GetRandomString(n, lower), StringCount) +} + +func setRandSeed() { + RndSeed++ + rand.Seed(RndSeed) +} + +const schemaBytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +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 +} + +func GetGroupKinds() []schema.CommandGroupKind { + setRandSeed() + return []schema.CommandGroupKind{schema.BuildCommandGroupKind, schema.RunCommandGroupKind, schema.TestCommandGroupKind, schema.DebugCommandGroupKind} +} + +func GetRandomGroupKind() schema.CommandGroupKind { + setRandSeed() + return GetGroupKinds()[rand.Intn(len(GetGroupKinds()))] +} + +func GetBinaryDecision() bool { + return GetRandomDecision(1, 1) +} + +func GetRandomDecision(success int, failure int) bool { + setRandSeed() + return rand.Intn(success+failure) > failure-1 +} + +func GetRandomNumber(max int) int { + setRandSeed() + return rand.Intn(max) + 1 +} + +func GetDevfile(fileName string) TestDevfile { + testDevfile := TestDevfile{} + testDevfile.SchemaDevFile = schema.Devfile{} + testDevfile.FileName = fileName + testDevfile.SchemaDevFile.SchemaVersion = "2.0.0" + testDevfile.SchemaParsed = false + return testDevfile +} + +func (devfile *TestDevfile) MapCommand(command GenericCommand) { + + if devfile.CommandMap == nil { + devfile.CommandMap = make(map[string]*GenericCommand) + } + LogMessage(fmt.Sprintf(" .......Add command to commandMap : %s", command.Id)) + devfile.CommandMap[command.Id] = &command +} + +func (devfile *TestDevfile) GetCommand(id string) *GenericCommand { + + genericCommand := devfile.CommandMap[id] + return genericCommand + +} + +func (devfile *TestDevfile) MapComponent(component GenericComponent) { + + if devfile.ComponentMap == nil { + devfile.ComponentMap = make(map[string]*GenericComponent) + } + LogMessage(fmt.Sprintf(" .......Add component to componentMap : %s", component.Name)) + devfile.ComponentMap[component.Name] = &component +} + +func (devfile *TestDevfile) GetComponent(name string) *GenericComponent { + return devfile.ComponentMap[name] +} + +func (devfile *TestDevfile) CreateDevfile(useParser bool) error { + var err error + if useParser { + LogMessage(fmt.Sprintf(" .......use Parser to write devfile %s", devfile.FileName)) + newDevfile, err := devfileData.NewDevfileData(devfile.SchemaDevFile.SchemaVersion) + if err != nil { + LogMessage(fmt.Sprintf(" ..... ERROR: creating new devfile : %v", err)) + } else { + newDevfile.SetSchemaVersion(devfile.SchemaDevFile.SchemaVersion) + + // add the commands to new devfile + for _, command := range devfile.SchemaDevFile.Commands { + newDevfile.AddCommands(command) + } + // add components to the new devfile + newDevfile.AddComponents(devfile.SchemaDevFile.Components) + + ctx := devfileCtx.NewDevfileCtx(devfile.FileName) + + err = ctx.SetAbsPath() + if err != nil { + LogMessage(fmt.Sprintf(" ..... ERROR: setting devfile path : %v", err)) + } else { + devObj := parser.DevfileObj{ + Ctx: ctx, + Data: newDevfile, + } + err = devObj.WriteYamlDevfile() + if err != nil { + LogMessage(fmt.Sprintf(" ..... ERROR: wriring devfile : %v", err)) + } + } + + } + } else { + LogMessage(fmt.Sprintf(" .......marshall and write devfile %s", devfile.FileName)) + c, err := yaml.Marshal(&(devfile.SchemaDevFile)) + + if err == nil { + err = ioutil.WriteFile(devfile.FileName, c, 0644) + } + } + if err == nil { + devfile.SchemaParsed = false + } + return err +} + +func (devfile *TestDevfile) ParseSchema() error { + + var err error + if !devfile.SchemaParsed { + LogMessage(fmt.Sprintf(" -> Parse and Validate %s : ", devfile.FileName)) + devfile.ParsedSchemaObj, err = devfilepkg.ParseAndValidate(devfile.FileName) + if err != nil { + LogMessage(fmt.Sprintf(" ......ERROR from ParseAndValidate %v : ", err)) + } + devfile.SchemaParsed = true + } + return err +} + +func (devfile TestDevfile) Verify() error { + + LogMessage(fmt.Sprintf("Verify %s : ", devfile.FileName)) + + err := devfile.ParseSchema() + + if err == nil { + LogMessage(fmt.Sprintf(" -> Get commands %s : ", devfile.FileName)) + commands, _ := devfile.ParsedSchemaObj.Data.GetCommands(common.DevfileOptions{}) + if commands != nil && len(commands) > 0 { + err = devfile.VerifyCommands(commands) + } else { + LogMessage(fmt.Sprintf(" No command found in %s : ", devfile.FileName)) + } + } + + if err == nil { + LogMessage(fmt.Sprintf(" -> Get components %s : ", devfile.FileName)) + components, _ := devfile.ParsedSchemaObj.Data.GetComponents(common.DevfileOptions{}) + if components != nil && len(components) > 0 { + err = devfile.VerifyComponents(components) + } else { + LogMessage(fmt.Sprintf(" No components found in %s : ", devfile.FileName)) + } + } + + return err + +} + +func (devfile TestDevfile) EditCommands() error { + + LogMessage(fmt.Sprintf("Edit %s : ", devfile.FileName)) + + err := devfile.ParseSchema() + if err == nil { + LogMessage(fmt.Sprintf(" -> Get commands %s : ", devfile.FileName)) + commands, _ := devfile.ParsedSchemaObj.Data.GetCommands(common.DevfileOptions{}) + for _, command := range commands { + err = devfile.UpdateCommand(&command) + if err != nil { + LogMessage(fmt.Sprintf(" ..... ERROR: updating command : %v", err)) + } else { + LogMessage(fmt.Sprintf(" ..... Update command in Parser : %s", command.Id)) + devfile.ParsedSchemaObj.Data.UpdateCommand(command) + } + } + LogMessage(fmt.Sprintf(" ..... Write updated file to yaml : %s", devfile.FileName)) + devfile.ParsedSchemaObj.WriteYamlDevfile() + devfile.SchemaParsed = false + } else { + LogMessage(fmt.Sprintf(" ..... ERROR: from parser : %v", err)) + } + return err + +} + +func (devfile TestDevfile) EditComponents() error { + + LogMessage(fmt.Sprintf("Edit %s : ", devfile.FileName)) + + err := devfile.ParseSchema() + if err == nil { + LogMessage(fmt.Sprintf(" -> Get commands %s : ", devfile.FileName)) + components, _ := devfile.ParsedSchemaObj.Data.GetComponents(common.DevfileOptions{}) + for _, component := range components { + err = devfile.UpdateComponent(&component) + if err != nil { + LogMessage(fmt.Sprintf(" ..... ERROR: updating component : %v", err)) + } else { + LogMessage(fmt.Sprintf(" ..... Update component in Parser : %s", component.Name)) + devfile.ParsedSchemaObj.Data.UpdateComponent(component) + } + } + LogMessage(fmt.Sprintf(" ..... Write updated file to yaml : %s", devfile.FileName)) + devfile.ParsedSchemaObj.WriteYamlDevfile() + devfile.SchemaParsed = false + } else { + LogMessage(fmt.Sprintf(" ..... ERROR: from parser : %v", err)) + } + return err + +}