Skip to content

Commit

Permalink
MPL-32 Moved to CPS tests by default
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergei Parshev committed Nov 6, 2019
1 parent 807fc47 commit 8c434f5
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Grid Dynamics International, Inc. All Rights Reserved
// Copyright (c) 2019 Grid Dynamics International, Inc. All Rights Reserved
// https://www.griddynamics.com
//
// Classification level: Public
Expand Down Expand Up @@ -73,20 +73,21 @@ class BuildTest extends MPLTestBase {
assertThat(helper.callStack)
.filteredOn { c -> c.methodName == 'tool' }
.filteredOn { c -> c.argsToString().contains('Maven 3') }
.as('Maven 3 tool used')
.isNotEmpty()

assertThat(helper.callStack)
.as('Shell execution should contain mvn command and default clean install')
.filteredOn { c -> c.methodName == 'sh' }
.filteredOn { c -> c.argsToString().startsWith('mvn') }
.filteredOn { c -> c.argsToString().contains('clean install') }
.as('Shell execution should contain mvn command and default clean install')
.isNotEmpty()

assertThat(helper.callStack)
.as('Default mvn run without settings provided')
.filteredOn { c -> c.methodName == 'sh' }
.filteredOn { c -> c.argsToString().startsWith('mvn') }
.filteredOn { c -> ! c.argsToString().contains('-s ') }
.as('Default mvn run without settings provided')
.isNotEmpty()

assertJobStatusSuccess()
Expand All @@ -103,9 +104,9 @@ class BuildTest extends MPLTestBase {
printCallStack()

assertThat(helper.callStack)
.as('Changing maven tool name')
.filteredOn { c -> c.methodName == 'tool' }
.filteredOn { c -> c.argsToString().contains('Maven 2') }
.as('Changing maven tool name')
.isNotEmpty()

assertJobStatusSuccess()
Expand All @@ -122,9 +123,9 @@ class BuildTest extends MPLTestBase {
printCallStack()

assertThat(helper.callStack)
.as('Providing setings file should set the maven opetion')
.filteredOn { c -> c.methodName == 'sh' }
.filteredOn { c -> c.argsToString().contains("-s '/test-settings.xml'") }
.as('Providing setings file should set the maven opetion')
.isNotEmpty()

assertJobStatusSuccess()
Expand Down
49 changes: 5 additions & 44 deletions test/groovy/com/griddynamics/devops/mpl/testing/MPLTestBase.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,18 @@

package com.griddynamics.devops.mpl.testing

import com.lesfurets.jenkins.unit.BasePipelineTest
import com.griddynamics.devops.mpl.testing.MPLTestBaseSetup
import com.lesfurets.jenkins.unit.cps.BasePipelineTestCPS

import com.griddynamics.devops.mpl.MPLConfig
import com.griddynamics.devops.mpl.MPLManager
import com.griddynamics.devops.mpl.Helper
import com.griddynamics.devops.mpl.testing.MPLTestHelper

import java.security.AccessController
import java.security.PrivilegedAction
import org.codehaus.groovy.runtime.InvokerHelper

abstract class MPLTestBase extends BasePipelineTest {
abstract class MPLTestBase extends BasePipelineTestCPS {
MPLTestBase() {
helper = new MPLTestHelper()
}

void setUp() throws Exception {
super.setUp()

// Overriding Helper to find the right resources in the loaded libs
Helper.metaClass.static.getModulesList = { String path ->
def modules = []
def resourcesFolder = helper.getLibraryClassLoader().getResource('.').getFile()
MPLManager.instance.getModulesLoadPaths().each { modulesPath ->
def libPath = modulesPath + '/' + path
helper.getLibraryClassLoader().getResources(libPath).each { res ->
def libname = res.getFile().substring(resourcesFolder.length())
libname = libname.substring(0, Math.max(libname.indexOf('@'), 0))
modules += [[libname + '/' + libPath, res.text]]
}
}
return modules
}

// Replacing runModule function to mock it
Helper.metaClass.static.runModule = { String source, String path, Map vars = [:] ->
def binding = new Binding()
this.binding.variables.each { k, v -> binding.setVariable(k, v) }
vars.each { k, v -> binding.setVariable(k, v) }
def loader = AccessController.doPrivileged(new PrivilegedAction<GroovyClassLoader>() {
public GroovyClassLoader run() {
return new GroovyClassLoader(helper.getLibraryClassLoader(), helper.getLibraryConfig())
}
})
def script = InvokerHelper.createScript(loader.parseClass(new GroovyCodeSource(source, path, '/mpl/modules'), false), binding)
script.metaClass.invokeMethod = helper.getMethodInterceptor()
script.metaClass.static.invokeMethod = helper.getMethodInterceptor()
script.metaClass.methodMissing = helper.getMethodMissingInterceptor()
script.run()
}

// Show the dump of the configuration during unit tests execution
Helper.metaClass.static.configEntrySet = { Map config -> config.entrySet() }
MPLTestBaseSetup.setUp(helper, this.binding)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Copyright (c) 2018 Grid Dynamics International, Inc. All Rights Reserved
// https://www.griddynamics.com
//
// Classification level: Public
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// $Id: $
// @Project: MPL
// @Description: Shared Jenkins Modular Pipeline Library
//

package com.griddynamics.devops.mpl.testing

import com.griddynamics.devops.mpl.testing.MPLTestBaseSetup
import com.lesfurets.jenkins.unit.BasePipelineTest

import com.griddynamics.devops.mpl.testing.MPLTestHelper

// It's not recommended to use non-cps class for testing - use MPLTestBase instead
abstract class MPLTestBaseNonCPS extends BasePipelineTest {
MPLTestBaseNonCPS() {
helper = new MPLTestHelperNonCPS()
}

void setUp() throws Exception {
super.setUp()
MPLTestBaseSetup.setUp(helper, this.binding)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// Copyright (c) 2019 Grid Dynamics International, Inc. All Rights Reserved
// https://www.griddynamics.com
//
// Classification level: Public
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// $Id: $
// @Project: MPL
// @Description: Shared Jenkins Modular Pipeline Library
//

package com.griddynamics.devops.mpl.testing

import com.griddynamics.devops.mpl.MPLManager
import com.griddynamics.devops.mpl.Helper

import org.codehaus.groovy.runtime.InvokerHelper

abstract class MPLTestBaseSetup {
static void setUp(helper, context) throws Exception {
// Overriding Helper to find the right resources in the loaded libs
Helper.metaClass.static.getModulesList = { String path ->
def modules = []
def resourcesFolder = helper.getLibraryClassLoader().getResource('.').getFile()
MPLManager.instance.getModulesLoadPaths().each { modulesPath ->
def libPath = modulesPath + '/' + path
helper.getLibraryClassLoader().getResources(libPath).each { res ->
def libname = res.getFile().substring(resourcesFolder.length())
libname = libname.substring(0, Math.max(libname.indexOf('@'), 0))
modules += [[libname + '/' + libPath, res.text]]
}
}
return modules
}

// Replacing runModule function to mock it
Helper.metaClass.static.runModule = { String source, String path, Map vars = [:] ->
def binding = new Binding()
context.variables.each { k, v -> binding.setVariable(k, v) }
vars.each { k, v -> binding.setVariable(k, v) }
def script = InvokerHelper.createScript(helper.getLibraryClassLoader().parseClass(new GroovyCodeSource(source, path, '/mpl/modules'), false), binding)
script.metaClass.invokeMethod = helper.getMethodInterceptor()
script.metaClass.static.invokeMethod = helper.getMethodInterceptor()
script.metaClass.methodMissing = helper.getMethodMissingInterceptor()
script.run()
}

// Show the dump of the configuration during unit tests execution
Helper.metaClass.static.configEntrySet = { Map config -> config.entrySet() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@

package com.griddynamics.devops.mpl.testing

import com.lesfurets.jenkins.unit.PipelineTestHelper
import com.lesfurets.jenkins.unit.cps.PipelineTestHelperCPS
import com.lesfurets.jenkins.unit.MethodSignature

import com.cloudbees.groovy.cps.impl.CpsCallableInvocation
import com.cloudbees.groovy.cps.Envs
import com.cloudbees.groovy.cps.Continuation

@groovy.transform.InheritConstructors
class MPLTestHelper extends PipelineTestHelper {
class MPLTestHelper extends PipelineTestHelperCPS {
public getLibraryConfig() {
gse.getConfig()
}
Expand All @@ -40,4 +44,33 @@ class MPLTestHelper extends PipelineTestHelper {
return // Skipping methods already existing in the list
allowedMethodCallbacks.put(methodSignature, closure)
}

// To make NotSerializableException more readable
@Override
Object callClosure(Closure closure, Object[] args = null) {
try {
callClosure2(closure, args)
} catch(CpsCallableInvocation e) {
def next = e.invoke(Envs.empty(), null, Continuation.HALT)
while(next.yield==null) {
try {
this.roundtripSerialization(next.e)
} catch (exception) {
throw new Exception("Exception during serialization in `${next.e.closureOwner().class.name}` for class ${exception.getMessage()}", exception)
}
next = next.step()
}
return next.yield.replay()
}
}

Object callClosure2(Closure closure, Object[] args = null) {
if (!args) {
return closure.call()
} else if (args.size() > closure.maximumNumberOfParameters) {
return closure.call(args)
} else {
return closure.call(*args)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// Copyright (c) 2018 Grid Dynamics International, Inc. All Rights Reserved
// https://www.griddynamics.com
//
// Classification level: Public
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// $Id: $
// @Project: MPL
// @Description: Shared Jenkins Modular Pipeline Library
//

package com.griddynamics.devops.mpl.testing

import com.lesfurets.jenkins.unit.PipelineTestHelper
import com.lesfurets.jenkins.unit.MethodSignature

@groovy.transform.InheritConstructors
class MPLTestHelperNonCPS extends PipelineTestHelper {
public getLibraryConfig() {
gse.getConfig()
}
public getLibraryClassLoader() {
gse.groovyClassLoader
}

void registerAllowedMethod(MethodSignature methodSignature, Closure closure) {
if( isMethodAllowed(methodSignature.name, methodSignature.args) )
return // Skipping methods already existing in the list
allowedMethodCallbacks.put(methodSignature, closure)
}
}
13 changes: 8 additions & 5 deletions vars/MPLModule.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def call(String name = env.STAGE_NAME, cfg = null) {
cfg = cfg.clone()
else
cfg = MPLConfig.create(cfg)

// Trace of the running modules to find loops
// Also to make ability to use lib module from overridden one
def active_modules = MPLManager.instance.getActiveModules()
Expand All @@ -64,10 +64,13 @@ def call(String name = env.STAGE_NAME, cfg = null) {
module_src = readFile(project_path)
} else {
// Searching for the not executed module from the loaded libraries
module_src = Helper.getModulesList(module_path).find { it ->
module_path = "library:${it.first()}".toString()
! active_modules.contains(module_path)
}?.last()
def modules = Helper.getModulesList(module_path)
for( def i = 0; i < modules.size(); i++ ) {
if( ! active_modules.contains(module_path) ) {
module_path = "library:${modules[i].first()}".toString()
module_src = modules[i].last()
}
}
}

if( ! module_src )
Expand Down

0 comments on commit 8c434f5

Please sign in to comment.