1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-03-03 15:02:35 +02:00

A unit object set enhancement (#3273)

* change return after files are persisted & Change logging

* object set enhancement

* enhance object set

* Object set enhancement

* object set enhancement

* reduce object set

* adapt documentation

* adapt docu

* remove comment blocks

* remove comment blocks

* adapt object set usage

* adapt documentation to new object set usage

* adapt documentation to new object set usage & remove trailing spaces

* Adapt Unit tests

* Adapt Unit tests

* Adapt Unit tests

* cleanup code

* cleanup code

* refactor object set

* remove parallel tests

* reduce set basis

* adapt tests

* dummy commit

* adapt docu

* Update documentation/docs/steps/abapEnvironmentRunAUnitTest.md

* Update documentation/docs/steps/abapEnvironmentRunAUnitTest.md

* Update documentation/docs/steps/abapEnvironmentRunAUnitTest.md

Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com>
This commit is contained in:
Dominik Lendle 2021-12-21 15:24:54 +01:00 committed by GitHub
parent 51feacadbf
commit 9917b08ff8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 702 additions and 98 deletions

View File

@ -129,7 +129,7 @@ func triggerAUnitrun(config abapEnvironmentRunAUnitTestOptions, details abaputil
//Trigger AUnit run
var resp *http.Response
var bodyString = `<?xml version="1.0" encoding="UTF-8"?>` + metadataString + optionsString + objectSetString + `</aunit:run>`
var bodyString = `<?xml version="1.0" encoding="UTF-8"?>` + metadataString + optionsString + objectSetString
var body = []byte(bodyString)
if err == nil {
log.Entry().Debugf("Request Body: %s", bodyString)
@ -188,24 +188,25 @@ func buildAUnitTestBody(AUnitConfig AUnitConfig) (metadataString string, options
//Checks before building the XML body
if AUnitConfig.Title == "" {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run config. No title for the AUnit run has been provided. Please configure an appropriate title for the respective test run")
} else if AUnitConfig.Context == "" {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run config. No context for the AUnit run has been provided. Please configure an appropriate context for the respective test run")
} else if reflect.DeepEqual(AUnitOptions{}, AUnitConfig.Options) {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run config. No options have been provided. Please configure the options for the respective test run")
} else if reflect.DeepEqual(ObjectSet{}, AUnitConfig.ObjectSet) {
}
if AUnitConfig.Context == "" {
AUnitConfig.Context = "ABAP Environment Pipeline"
}
if reflect.DeepEqual(ObjectSet{}, AUnitConfig.ObjectSet) {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run object set config. No object set has been provided. Please configure the objects you want to be checked for the respective test run")
} else if len(AUnitConfig.ObjectSet) == 0 {
}
if len(AUnitConfig.ObjectSet) == 0 {
return "", "", "", fmt.Errorf("Error while parsing AUnit test run object set config. No object set has been provided. Please configure the set of objects you want to be checked for the respective test run")
}
//Build Options
optionsString += buildAUnitOptionsString(AUnitConfig)
//Build metadata string
metadataString += `<aunit:run title="` + AUnitConfig.Title + `" context="` + AUnitConfig.Context + `" xmlns:aunit="http://www.sap.com/adt/api/aunit">`
//Build Options
optionsString += buildAUnitOptionsString(AUnitConfig)
//Build Object Set
objectSetString += buildAUnitObjectSetString(AUnitConfig)
objectSetString += `</aunit:run>`
return metadataString, optionsString, objectSetString, nil
}
@ -231,56 +232,133 @@ func buildAUnitOptionsString(AUnitConfig AUnitConfig) (optionsString string) {
optionsString += `<aunit:options>`
if AUnitConfig.Options.Measurements != "" {
optionsString += `<aunit:measurements type="` + AUnitConfig.Options.Measurements + `"/>`
} else {
optionsString += `<aunit:measurements type="none"/>`
}
//We assume there must be one scope configured
optionsString += `<aunit:scope`
if AUnitConfig.Options.Scope.OwnTests != nil {
optionsString += ` ownTests="` + fmt.Sprintf("%v", *AUnitConfig.Options.Scope.OwnTests) + `"`
} else {
optionsString += ` ownTests="true"`
}
if AUnitConfig.Options.Scope.ForeignTests != nil {
optionsString += ` foreignTests="` + fmt.Sprintf("%v", *AUnitConfig.Options.Scope.ForeignTests) + `"`
} else {
optionsString += ` foreignTests="true"`
}
//We assume there must be one riskLevel configured
optionsString += `/><aunit:riskLevel`
if AUnitConfig.Options.RiskLevel.Harmless != nil {
optionsString += ` harmless="` + fmt.Sprintf("%v", *AUnitConfig.Options.RiskLevel.Harmless) + `"`
} else {
optionsString += ` harmless="true"`
}
if AUnitConfig.Options.RiskLevel.Dangerous != nil {
optionsString += ` dangerous="` + fmt.Sprintf("%v", *AUnitConfig.Options.RiskLevel.Dangerous) + `"`
} else {
optionsString += ` dangerous="true"`
}
if AUnitConfig.Options.RiskLevel.Critical != nil {
optionsString += ` critical="` + fmt.Sprintf("%v", *AUnitConfig.Options.RiskLevel.Critical) + `"`
} else {
optionsString += ` critical="true"`
}
//We assume there must be one duration time configured
optionsString += `/><aunit:duration`
if AUnitConfig.Options.Duration.Short != nil {
optionsString += ` short="` + fmt.Sprintf("%v", *AUnitConfig.Options.Duration.Short) + `"`
} else {
optionsString += ` short="true"`
}
if AUnitConfig.Options.Duration.Medium != nil {
optionsString += ` medium="` + fmt.Sprintf("%v", *AUnitConfig.Options.Duration.Medium) + `"`
} else {
optionsString += ` medium="true"`
}
if AUnitConfig.Options.Duration.Long != nil {
optionsString += ` long="` + fmt.Sprintf("%v", *AUnitConfig.Options.Duration.Long) + `"`
} else {
optionsString += ` long="true"`
}
optionsString += `/></aunit:options>`
return optionsString
}
func buildOSLObjectSets(multipropertyset MultiPropertySet) (objectSetString string) {
objectSetString += writeObjectSetProperties(multipropertyset)
return objectSetString
}
func writeObjectSetProperties(set MultiPropertySet) (objectSetString string) {
for _, packages := range set.PackageNames {
objectSetString += `<osl:package name="` + packages.Name + `"/>`
}
for _, objectTypeGroup := range set.ObjectTypeGroups {
objectSetString += `<osl:objectTypeGroup name="` + objectTypeGroup.Name + `"/>`
}
for _, objectType := range set.ObjectTypes {
objectSetString += `<osl:objectType name="` + objectType.Name + `"/>`
}
for _, owner := range set.Owners {
objectSetString += `<osl:owner name="` + owner.Name + `"/>`
}
for _, releaseState := range set.ReleaseStates {
objectSetString += `<osl:releaseState value="` + releaseState.Value + `"/>`
}
for _, version := range set.Versions {
objectSetString += `<osl:version value="` + version.Value + `"/>`
}
for _, applicationComponent := range set.ApplicationComponents {
objectSetString += `<osl:applicationComponent name="` + applicationComponent.Name + `"/>`
}
for _, component := range set.SoftwareComponents {
objectSetString += `<osl:softwareComponent name="` + component.Name + `"/>`
}
for _, transportLayer := range set.TransportLayers {
objectSetString += `<osl:transportLayer name="` + transportLayer.Name + `"/>`
}
for _, language := range set.Languages {
objectSetString += `<osl:language value="` + language.Value + `"/>`
}
for _, sourceSystem := range set.SourceSystems {
objectSetString += `<osl:sourceSystem name="` + sourceSystem.Name + `"/>`
}
return objectSetString
}
func buildAUnitObjectSetString(AUnitConfig AUnitConfig) (objectSetString string) {
//Build ObjectSets {
//Build ObjectSets
for _, s := range AUnitConfig.ObjectSet {
//Write object set
objectSetString += `<osl:objectSet xsi:type="` + s.Type + `" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">`
for _, t := range s.Set {
objectSetString += `<osl:set xsi:type="` + t.Type + `">`
for _, packageSet := range t.PackageSet {
objectSetString += `<osl:package name="` + packageSet.Name + `" includeSubpackages="` + fmt.Sprintf("%v", *packageSet.IncludeSubpackages) + `"/>`
}
for _, flatObjectSet := range t.FlatObjectSet {
objectSetString += `<osl:object name="` + flatObjectSet.Name + `" type="` + fmt.Sprintf("%v", *&flatObjectSet.Type) + `"/>`
}
objectSetString += `</osl:set>`
if s.Type == "" {
s.Type = "multiPropertySet"
}
if s.Type != "multiPropertySet" {
log.Entry().Infof("Wrong configuration has been detected: %s has been used. This is currently not supported and this set will not be included in this run. Please check the step documentation for more information", s.Type)
} else {
objectSetString += `<osl:objectSet xsi:type="` + s.Type + `" xmlns:osl="http://www.sap.com/api/osl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">`
objectSetString += `</osl:objectSet>`
if !(reflect.DeepEqual(s.PackageNames, AUnitPackage{})) || !(reflect.DeepEqual(s.SoftwareComponents, SoftwareComponents{})) {
//To ensure Scomps and packages can be assigned on this level
mps := MultiPropertySet{
PackageNames: s.PackageNames,
SoftwareComponents: s.SoftwareComponents,
}
objectSetString += buildOSLObjectSets(mps)
}
objectSetString += buildOSLObjectSets(s.MultiPropertySet)
if !(reflect.DeepEqual(s.MultiPropertySet, MultiPropertySet{})) {
log.Entry().Info("Wrong configuration has been detected: MultiPropertySet has been used. Please note that there is no official documentation for this usage. Please check the step documentation for more information")
}
for _, t := range s.Set {
log.Entry().Infof("Wrong configuration has been detected: %s has been used. This is currently not supported and this set will not be included in this run. Please check the step documentation for more information", t.Type)
}
objectSetString += `</osl:objectSet>`
}
}
return objectSetString
}
@ -391,6 +469,7 @@ func parseAUnitResult(body []byte, aunitResultFileName string, generateHTML bool
log.Entry().Infof(`Here are the results for the AUnit test run '%s' executed by User %s on System %s in Client %s at %s. The AUnit run took %s seconds and contains %s tests with %s failures, %s errors, %s skipped and %s assert findings`, parsedXML.Title, parsedXML.System, parsedXML.ExecutedBy, parsedXML.Client, parsedXML.Timestamp, parsedXML.Time, parsedXML.Tests, parsedXML.Failures, parsedXML.Errors, parsedXML.Skipped, parsedXML.Asserts)
for _, s := range parsedXML.Testsuite.Testcase {
//Log Infos for testcase
//HTML Procesing can be done here
for _, failure := range s.Failure {
log.Entry().Debugf("%s, %s: %s found by %s", failure.Type, failure.Message, failure.Message, s.Classname)
}
@ -489,34 +568,122 @@ type Duration struct {
//ObjectSet in form of packages and software components to be checked
type ObjectSet struct {
Type string `json:"type,omitempty"`
Set []Set `json:"set,omitempty"`
PackageNames []AUnitPackage `json:"packages,omitempty"`
SoftwareComponents []SoftwareComponents `json:"softwarecomponents,omitempty"`
Type string `json:"type,omitempty"`
MultiPropertySet MultiPropertySet `json:"multipropertyset,omitempty"`
Set []Set `json:"set,omitempty"`
}
//Set in form of packages and software components to be checked
//MultiPropertySet that can possibly contain any subsets/object of the OSL
type MultiPropertySet struct {
Type string `json:"type,omitempty"`
PackageNames []AUnitPackage `json:"packages,omitempty"`
ObjectTypeGroups []ObjectTypeGroup `json:"objecttypegroup,omitempty"`
ObjectTypes []ObjectType `json:"objecttypes,omitempty"`
Owners []Owner `json:"owner,omitempty"`
ReleaseStates []ReleaseState `json:"releasestate,omitempty"`
Versions []Version `json:"version,omitempty"`
ApplicationComponents []ApplicationComponent `json:"applicationcomponent,omitempty"`
SoftwareComponents []SoftwareComponents `json:"softwarecomponents,omitempty"`
TransportLayers []TransportLayer `json:"transportlayer,omitempty"`
Languages []Language `json:"language,omitempty"`
SourceSystems []SourceSystem `json:"sourcesystem,omitempty"`
}
//Set
type Set struct {
//Set []Set `json:"set,omitempty"`
Type string `json:"type,omitempty"`
PackageSet []AUnitPackage `json:"package,omitempty"`
FlatObjectSet []AUnitObject `json:"object,omitempty"`
/*FlatSet []FlatObjectSet `json:"flatobjectset,omitempty"`
ObjectTypeSet []ObjectTypeSet `json:"objecttypeset,omitempty"`
ComponentSet []ComponentSet `json:"componentset,omitempty"`
TransportSet []TransportSet `json:"transportset,omitempty"`*/
Type string `json:"type,omitempty"`
Set []Set `json:"set,omitempty"`
PackageSet []AUnitPackageSet `json:"package,omitempty"`
FlatObjectSet []AUnitFlatObjectSet `json:"object,omitempty"`
ComponentSet []AUnitComponentSet `json:"component,omitempty"`
TransportSet []AUnitTransportSet `json:"transport,omitempty"`
ObjectTypeSet []AUnitObjectTypeSet `json:"objecttype,omitempty"`
}
//AUnitPackage in form of packages and software components to be checked
type AUnitPackage struct {
//AUnitPackageSet in form of packages to be checked
type AUnitPackageSet struct {
Name string `json:"name,omitempty"`
IncludeSubpackages *bool `json:"includesubpackages,omitempty"`
}
//AUnitObject in form of packages and software components to be checked
type AUnitObject struct {
//AUnitFlatObjectSet
type AUnitFlatObjectSet struct {
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
}
//AUnitComponentSet in form of software components to be checked
type AUnitComponentSet struct {
Name string `json:"name,omitempty"`
}
//AUnitTransportSet in form of transports to be checked
type AUnitTransportSet struct {
Number string `json:"number,omitempty"`
}
//AUnitObjectTypeSet
type AUnitObjectTypeSet struct {
Name string `json:"name,omitempty"`
}
//AUnitPackage for MPS
type AUnitPackage struct {
Name string `json:"name,omitempty"`
}
//ObjectTypeGroup
type ObjectTypeGroup struct {
Name string `json:"name,omitempty"`
}
//ObjectType
type ObjectType struct {
Name string `json:"name,omitempty"`
}
//Owner
type Owner struct {
Name string `json:"name,omitempty"`
}
//ReleaseState
type ReleaseState struct {
Value string `json:"value,omitempty"`
}
//Version
type Version struct {
Value string `json:"value,omitempty"`
}
//ApplicationComponent
type ApplicationComponent struct {
Name string `json:"name,omitempty"`
}
//SoftwareComponents
type SoftwareComponents struct {
Name string `json:"name,omitempty"`
}
//TransportLayer
type TransportLayer struct {
Name string `json:"name,omitempty"`
}
//Language
type Language struct {
Value string `json:"value,omitempty"`
}
//SourceSystem
type SourceSystem struct {
Name string `json:"name,omitempty"`
}
//
// AUnit Run Structure
//

File diff suppressed because one or more lines are too long

View File

@ -106,7 +106,7 @@ stages:
#### aunitConfig.yml
Please note that it is recommended to specify each development package you want to be checked as it is not possible to specify structure packages within the `aUnitConfig.yml` file. You can specify complete development packages using the `includesubpackages: false` parameter like in below example configuration.
If you want to test complete software components please specify the `aUnitConfig.yml` file like in below example configuration. This configuration will test the software components `Z_TEST_SC` and `Z_TEST_SC2`:
```yaml
title: My AUnit run
@ -125,10 +125,7 @@ options:
medium: true
long: true
objectSet:
- type: unionSet
set:
- type: packageSet
package:
- name: MY_PACKAGE
includeSubpackages: false
- softwarecomponents:
- name: Z_TEST_SC
- name: Z_TEST_SC2
```

View File

@ -2,6 +2,8 @@
## ${docGenDescription}
!!! Currently the Object Set configuration is limited to the usage of Multi Property Sets. Please note that other sets besides the Multi Property Set will not be included in the ABAP Unit test runs. You can see an example of the Multi Property Sets with all configurable properties. However, we strongly reccommend to only specify packages and software components like in the first two examples of the section `AUnit config file example`.
## Prerequisites
* A SAP BTP, ABAP environment system is available. On this system, a [Communication User](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/0377adea0401467f939827242c1f4014.html), a [Communication System](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/1bfe32ae08074b7186e375ab425fb114.html) and a [Communication Arrangement](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/a0771f6765f54e1c8193ad8582a32edb.html) is setup for the Communication Scenario “SAP BTP, ABAP Environment - Software Component Test Integration (SAP_COM_0735)“. This can be done manually through the respective applications on the SAP BTP, ABAP environment system or through creating a Service Key for the system on Cloud Foundry with the parameters {“scenario_id”: “SAP_COM_0735", “type”: “basic”}. In a pipeline, you can do this with the step [cloudFoundryCreateServiceKey](https://sap.github.io/jenkins-library/steps/cloudFoundryCreateServiceKey/).
@ -84,10 +86,22 @@ To trigger the AUnit run an AUnit config file `aUnitConfig.yml` will be needed.
The following section contains an example of an `aUnitConfig.yml` file.
This file must be stored in the same Git folder where the `Jenkinsfile` is stored to run the pipeline. This repository containing the `Jenkinsfile` must be taken as a SCM in the Jenkins pipeline to run the pipeline.
You can specify an object set containing the objects that should be checked. These can be for example packages, classes or transport requests containing test classes that can be executed. This must be in the same format as below example for a `aUnitConfig.yml` file.
Note that if you specify a package inside a packageSet to be checked for each package that has to be checked you can configure if you want the subpackages to be included in checks or not. We recommend you to specify each development package you want to be checked as it is not possible to specify structure packages within the `aUnitConfig.yml` file. You can specify complete development packages using the `includesubpackages: false` parameter.
You can specify a Multi Property Set containing multiple properties that should be checked. Each property that is specified in the Multi Property Set acts like an additional rule.
This means if you specify e.g. a Multi Property Set containing the owner and package properties that an ABAP Unit test run will be started testing all objects belonging to this owner inside of the given package. If you additionally define the Version to be `ACTIVE` for the ABAP Unit test run inside of the Multi Property Set, only objects belonging to this owner which are active inside of the package would be tested.
This must be in the same format as below examples for a `aUnitConfig.yml` file.
Note that if you want to check complete software components we recommend to use the `softwareComponent` property over the `package` property.
See below example for an `aUnitConfig.yml` file containing a package to be checked:
See below example for an `aUnitConfig.yml` file containing a minimal configuration for the software component `/DMO/SWC` to be checked:
```yaml
title: My AUnit run
context: My unit tests
objectset:
- softwareComponents:
- name: /DMO/SWC
```
See below example for an `aUnitConfig.yml` file with the configured options containing the package `Z_TEST_PACKAGE` to be checked:
```yaml
title: My AUnit run
@ -105,16 +119,12 @@ options:
short: true
medium: true
long: true
objectSet:
- type: unionSet
set:
- type: packageSet
package:
- name: my_package
includeSubpackages: false
objectset:
- packages:
- name: Z_TEST_PACKAGE
```
The following example of an `aUnitConfig.yml` file containing one class and one interface to be checked:
The following example of an `aUnitConfig.yml` file containing the software component `Z_TESTSC` to be checked:
```yaml
title: My AUnit run
@ -132,13 +142,56 @@ options:
short: true
medium: true
long: true
objectSet:
- type: unionSet
set:
- type: flatObjectSet
object:
- name: my_class
type: CLAS
- name: my_interface
type: INTF
objectset:
- softwarecomponents:
- name: Z_TESTSC
```
The following example of an `aUnitConfig.yml` file contains all possible properties of the Multi Property Set that can be used. Please take note that this is not the reccommended approach. If you want to check packages or software components please use the two above examples. The usage of the Multi Property Set is only reccommended for ABAP Unit tests that require these rules for the test execution. There is no official documentation on the usage of the Multi Property Set.
```yaml
title: My AUnit run
context: My unit tests
options:
measurements: none
scope:
ownTests: true
foreignTests: true
riskLevel:
harmless: true
dangerous: true
critical: true
duration:
short: true
medium: true
long: true
objectset:
- type: multiPropertySet
multipropertyset:
owner:
- name: demoOwner
softwarecomponents:
- component:
name: demoSoftwareComponent
version:
- value: ACTIVE
packages:
- package:
name: demoPackage
objectnamepattern:
- value: 'ZCL_*'
language:
- value: EN
sourcesystem:
- name: H01
objecttype:
- name: CLAS
objecttypegroup:
- name: CLAS
releasestate:
- value: RELEASED
applicationcomponent:
- name: demoApplicationComponent
transportlayer:
- name: H01
```