Cannot import package in unit tests for a Jenkins Shared Library - jenkins

I'm attempting to create unit tests for a JenkinsShared library using Gradle in order to run the test tasks.
I've followed this tutorial which upon conclusion one has a working test suite for a shared library for functions within the vars folder (with the unit tests in src/test/groovy/*Test.groovy).
However, in our internal shared jenkins library we followed a more object oriented style and isolated functionality into a package of classes in the format: src/org/company/*.groovy.
The problem arises when attempting to import said package into a unit test class. In the tutorial, the functions are imported using the loadScript method this method fails when loading a class which is dependent on another file.
Take the class:
package tests
import org.junit.*
import com.lesfurets.jenkins.unit.*
import static groovy.test.GroovyAssert.*
import org.company.UtilFactory
class UtilFactoryTest extends BasePipelineTest {
#Test
void testCall() {
def util = UtilFactory.getUtil("hello")
assertEquals true, true
}
}
src/org/company/UtilFactory.groovy
package org.company
class UtilFactory implements Serializable {
static Util instance
static Util getUtil(script=null) {
if (!(UtilFactory.instance)) {
if (!script) {
// Throws an exception if on the first call to getUtil the
// script parameter is null.
throw new ScriptUndefinedException("script parameter null on initial call to getUtil")
}
UtilFactory.instance = new Util(script)
}
return UtilFactory.instance
}
}
class ScriptUndefinedException extends Exception {
// Parameterless Constructor
public ScriptUndefinedException() {}
// Constructor that accepts a message
public ScriptUndefinedException(String message)
{
super(message);
}
}
Which gives me the exception:
jenkins-utilities/src/test/groovy/UtilFactoryTest.groovy: 7:
unable to resolve class org.company.UtilFactory
# line 7, column 1.
import org.company.UtilFactory
This may be more of a Gradle issue than a JenkinsShared Library. I've just spent a good portion of my day trying to figure out exactly what I'm doing wrong to no avail.
I would really appreciate any help to guide me in the right direction.

This library may be helpful getting your shared libraries to work in the unit test https://github.com/stchar/pipeline-sharedlib-testharness

Related

Grails 4 how to get an handle to artifacts in custom command

I need to build a custom command in a Grails 4 application (https://docs.grails.org/4.0.11/guide/single.html#creatingCustomCommands), and I need to get an handle to some Grails Services and Domain classes which I will query as needed.
The custom command skeleton is quite simple:
import grails.dev.commands.*
import org.apache.maven.artifact.Artifact
class HelloWorldCommand implements GrailsApplicationCommand {
boolean handle() {
return true
}
}
While the documentation says that a custom command has access to the whole application context, I haven't found any examples on how to get an handle of that and start accessing the various application artifacts.
Any hints?
EDIT: to add context and clarify the goal of the custom command in order for further recommendation/best practices/etc.: the command reads data from a file in a custom format, persist the data, and writes reports in another custom format.
Will eventually be replaced by a recurrent job, once the data will be available on demand from a third party REST API.
See the project at github.com/jeffbrown/marco-vittorini-orgeas-artifacts-cli.
grails-app/services/marco/vittorini/orgeas/artifacts/cli/GreetingService.groovy
package marco.vittorini.orgeas.artifacts.cli
class GreetingService {
String greeting = 'Hello World'
}
grails-app/commands/marco/vittorini/orgeas/artifacts/cli/HelloCommand.groovy
package marco.vittorini.orgeas.artifacts.cli
import grails.dev.commands.*
class HelloCommand implements GrailsApplicationCommand {
GreetingService greetingService
boolean handle() {
println greetingService.greeting
return true
}
}
EDIT:
I have added a commit at github.com/jeffbrown/marco-vittorini-orgeas-artifacts-cli/commit/49a846e3902073f8ea0539fcde550f6d002b9d89 which demonstrates accessing a domain class, which was part of the question I overlooked when writing the initial answer.

Add custom information to Spock Global Extension

I have configured Spock Global Extension and static class ErrorListener inside it. Works fine for test errors when I want to catch feature title and errors if they happen. But how can I add some custom information to the listener?
For example I have test that calls some API. In case it fails I want to add request/response body to the listener (and report it later). Obviously I have request/response inside the feature or I can get it. How can I pass this information to the Listener and read later in the handling code?
package org.example
import groovy.json.JsonSlurper
import org.spockframework.runtime.AbstractRunListener
import org.spockframework.runtime.extension.AbstractGlobalExtension
import org.spockframework.runtime.model.ErrorInfo
import org.spockframework.runtime.model.IterationInfo
import org.spockframework.runtime.model.SpecInfo
import spock.lang.Specification
class OpenBrewerySpec extends Specification{
def getBreweryTest(){
def breweryText = new URL('https://api.openbrewerydb.org/breweries/1').text
def breweryJson = new JsonSlurper().parseText(breweryText)
//TODO catch breweryText for test result reporting if it is possible
expect:
breweryJson.country == 'United States'
}
def cleanup() {
specificationContext.currentSpec.listeners
.findAll { it instanceof TestResultExtension.ErrorListener }
.each {
def errorInfo = (it as TestResultExtension.ErrorListener).errorInfo
if (errorInfo)
println "Test failure in feature '${specificationContext.currentIteration.name}', " +
"exception class ${errorInfo.exception.class.simpleName}"
else
println "Test passed in feature '${specificationContext.currentIteration.name}'"
}
}
}
class TestResultExtension extends AbstractGlobalExtension {
#Override
void visitSpec(SpecInfo spec) {
spec.addListener(new ErrorListener())
}
static class ErrorListener extends AbstractRunListener {
ErrorInfo errorInfo
#Override
void beforeIteration(IterationInfo iteration) {
errorInfo = null
}
#Override
void error(ErrorInfo error) {
errorInfo = error
}
}
}
Create file src/test/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalExtension
and place string "org.example.TestResultExtension" there to enable extension.
I am pretty sure you found my solution here. Then you also know that it is designed to know in a cleanup() methods if the test succeeded or failed because otherwise Spock does not make the information available. I do not understand why deliberately omitted that information and posted a fragment instead of the whole method or at least mentioned where your code snippet gets executed. That is not a helpful way of asking a question. Nobody would know except for me because I am the author of this global extension.
So now after having established that you are inside a cleanup() method, I can tell you: The information does not belong into the global extension because in the cleanup() method you have access to information from the test such as fields. Why don't you design your test in such a way that whatever information cleanup() needs it stored in a field as you would normally do without using any global extensions? The latter is only meant to help you establish the error status (passed vs. failed) as such.
BTW, I even doubt if you need additional information in the cleanup() method at all because its purpose it cleaning up, not reporting or logging anything. For that Spock has a reporting system which you can also write extensions for.
Sorry for not being more specific in my answer, but your question is equally unspecific. It is an instance of the XY problem, explaining how you think you should do something instead of explaining what you want to achieve. Your sample code omits important details, e.g. the core test code as such.

What's happening behind the scenes in XCTest's #testable?

I know that
#testable import MyModule
gives ability to explore non-public members of MyModule from a "test" (built with "testTarget") module MyModuleTests.
I need the same functionality in my "non-test" module. Not in production, just in debug mode.
My question is: do you know how to do this?
And related (I think, harder question): what magic is actually happening behind #testable?
To answer your question, for debugging purposes, you can actually use this. Let's say you have a workspace MyAwesomeWkspace and a project inside MyAwesomeProject.
Now, create a new framework aka module called MyAwesomeModule. Inside that module create a non-public class called Person.
If you try to use the class Person inside MyAwesomeProject by doing import MyAwesomeModule and then something like let p = Person() you will have an error.
But if you do #testable import MyAwesomeModule, the magic happens and you can now use the class.
Basically #testable allows you to test things that you didn't declare public. The annotation only works with import as you can see it here.
So in order to work, the target is compiled with -enable-testing so that you can have access to non-public members. At least based on what's here
Because, by default, the debug build configuration is compiled with -enable-testing, the example I showed you will work. But if you change the build config to release, you'll see an error saying Module .. was not compiled for testing since the release config is not built with the flag.
The Swift access control model, as described in the Access Control
section of The Swift Programming Language (Swift 4), prevents an
external entity from accessing anything declared as internal in an app
or framework. By default, to be able to access these items from your
test code, you would need to elevate their access level to at least
public, reducing the benefits of Swift’s type safety.
Xcode provides a two-part solution to this problem:
When you set the Enable Testability build setting to Yes, which is
true by default for test builds in new projects, Xcode includes the
-enable-testing flag during compilation. This makes the Swift entities declared in the compiled module eligible for a higher level of access.
When you add the #testable attribute to an import statement for a
module compiled with testing enabled, you activate the elevated access
for that module in that scope. Classes and class members marked as
internal or public behave as if they were marked open. Other entities
marked as internal act as if they were declared public.
More here
Late edit: One of the cool parts of swift is that is open source. So if you want to dive deep into the "magic", check it out: https://github.com/apple/swift
#testable import <module_name> and -enable-testing
[Swift access modifiers]
[Swift module]
consumer side uses #testable import -> producer side should use `-enable-testing` flag
producer side: enable -enable-testing
Enable Testability(ENABLE_TESTABILITY) - YES
Other Swift Flags(OTHER_SWIFT_FLAGS) - -enable-testing
consumer side: #testable
internal(default) and public access level for class is visible for current module as open
internal(default) access level for others(struct, enum) is visible for current module as public
If you build test schema(consumer) with #testable but producer doesn't include -enable-testing you get
Module '<module_name>' was not compiled for testing
Some experiments:
SomeModule
internal class SomeInternalClass {
internal func foo() { }
}
public class SomePublicClass {
public func foo() { }
}
internal class SomeInternalStruct {
internal func foo() { }
}
internal enum SomeInternalEnum: String {
case foo = "hello world"
}
Tests: If you omit #testable next errors will occur
import XCTest
#testable import ExperimentsTests
class ExperimentsTestsTests: XCTestCase {
func testExample() throws {
let someInternalStruct = SomeInternalStruct() //Cannot find 'SomeInternalStruct' in scope
someInternalStruct.foo()
let someInternalEnum = SomeInternalEnum(rawValue: "") //Cannot find 'SomeInternalEnum' in scope
SomeInternalEnum.foo //Cannot find 'SomeInternalEnum' in scope
}
class SomePublicSubClass: SomePublicClass { //Cannot inherit from non-open class 'SomePublicClass' outside of its defining module
override func foo() { } //Overriding non-open instance method outside of its defining module
}
class SomeInternalSubClass: SomeInternalClass { //Cannot find type 'SomeInternalClass' in scope
override func foo() { } //Method does not override any method from its superclass
}
}

Dart Can't get logging statements to compile

I'm trying to learn how to implement logging using the examples/tutorial in:
http://blog.dartwatch.com/2013/05/campaign-to-use-real-logging-instead-of.html#comment-form
But having imported the libraries this line in main will not compile because the class 'PrintHandler' is not recognized and Google has not been a help in this case. My server application consists of a main and three classes. I'm new at Dart. Below I've extracted the logging code that I added.
In what library is 'PrintHandler'? Is this a class I need to write?
library server;
import 'package:logging_handlers/logging_handlers_shared.dart';
import 'package:logging/logging.dart';
final _serverLogger = new Logger("server"); // top level logger
void main() {
Logger.root.onRecord.listen(new PrintHandler()); // default PrintHandler
_serverLogger.fine("Server created");
}
class A {
}
class B {
}
class C {
}
It looks like the class was changed to LogPrintHandler but the tutorial and documentation were not updated.

Using new Groovy Grape capability results in "unable to resolve class" error

I've tried to use the new Groovy Grape capability in Groovy 1.6-beta-2 but I get an error message;
unable to resolve class com.jidesoft.swing.JideSplitButton
from the Groovy Console (/opt/groovy/groovy-1.6-beta-2/bin/groovyConsole) when running the stock example;
import com.jidesoft.swing.JideSplitButton
#Grab(group='com.jidesoft', module='jide-oss', version='[2.2.1,)')
public class TestClassAnnotation {
public static String testMethod () {
return JideSplitButton.class.name
}
}
I even tried running the grape command line tool to ensure the library is imported. Like this;
$ /opt/groovy/groovy-1.6-beta-2/bin/grape install com.jidesoft jide-oss
which does install the library just fine. How do I get the code to run/compile correctly from the groovyConsole?
There is still some kinks in working out the startup/kill switch routine. For Beta-2 do this in it's own script first:
groovy.grape.Grape.initGrape()
Another issue you will run into deals with the joys of using an unbounded upper range. Jide-oss from 2.3.0 onward has been compiling their code to Java 6 bytecodes, so you will need to either run the console in Java 6 (which is what you would want to do for Swing anyway) or set an upper limit on the ranges, like so
import com.jidesoft.swing.JideSplitButton
#Grab(group='com.jidesoft', module='jide-oss', version='[2.2.1,2.3.0)')
public class TestClassAnnotation {
public static String testMethod () {
return JideSplitButton.class.name
}
}
new TestClassAnnotation().testMethod()
I finally got it working for Groovy Shell (1.6.5, JVM: 1.6.0_13). This should be documented better.
First at the command line...
grape install org.codehaus.groovy.modules.http-builder http-builder 0.5.0-RC2
Then in groovysh...
groovy:000> import groovy.grape.Grape
groovy:000> Grape.grab(group:'org.codehaus.groovy.modules.http-builder', module:'http-builder', version:'0.5.0-RC2')
groovy:000> def http= new groovyx.net.http.HTTPBuilder('http://rovio')
===> groovyx.net.http.HTTPBuilder#91520
The #grab is better used in a file than the shell.
Ok. Seems like this a short working demo (running from the groovyConsole)
groovy.grape.Grape.initGrape()
#Grab(group='com.jidesoft', module='jide-oss', version='[2.2.1,2.3.0)')
public class UsedToExposeAnnotationToComplier {}
com.jidesoft.swing.JideSplitButton.class.name
When run it produces
Result: "com.jidesoft.swing.JideSplitButton"
Very cool!!
The import statement must appear after the grabs.
Ps. At least one import statement must exists after the grabs
#Grab(group='com.jidesoft', module='jide-oss', version='[2.2.1,)')
import com.jidesoft.swing.JideSplitButton
public class TestClassAnnotation {
public static String testMethod () {
return JideSplitButton.class.name
}
}
Different example using latest RC-2 (note: Grab annotates createEmptyInts):
// create and use a primitive array
import org.apache.commons.collections.primitives.ArrayIntList
#Grab(group='commons-primitives', module='commons-primitives', version='1.0')
def createEmptyInts() { new ArrayIntList() }
def ints = createEmptyInts()
ints.add(0, 42)
assert ints.size() == 1
assert ints.get(0) == 42
Another example (note: Grab annotates getHtml):
// find the PDF links in the Java 1.5.0 documentation
#Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='0.9.7')
def getHtml() {
def parser = new XmlParser(new org.ccil.cowan.tagsoup.Parser())
parser.parse("http://java.sun.com/j2se/1.5.0/download-pdf.html")
}
html.body.'**'.a.#href.grep(~/.*\.pdf/).each{ println it }
Another example (note: Grab annotates getFruit):
// Google Collections example
import com.google.common.collect.HashBiMap
#Grab(group='com.google.code.google-collections', module='google-collect', version='snapshot-20080530')
def getFruit() { [grape:'purple', lemon:'yellow', orange:'orange'] as HashBiMap }
assert fruit.inverse().yellow == 'lemon'

Resources