Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 42 audit get automated code coverage working #234

Merged
merged 18 commits into from
Sep 4, 2024
Merged
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [0.8.1] UNRELEASED

- Fix bug in path extraction from S3 URIs

## [0.8.0] 2024-08-31

- Add and improve code coverage using jacoco
- Support S3 URIs as an overlay plugin
- Fix bug with pathless input URIs

## [0.7.17] UNRELEASED

- support ChunkedChecksums
Expand Down
16 changes: 13 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,21 @@ check:

.PHONY: clean test test-all all pkg-test tower-test

test: clean compile check #coverage
test: clean compile check verifyCoverage

test-nextflow: clean nextflow-git compile check #coverage
test-nextflow: clean nextflow-git compile check

test-all: clean compile-all check #coverage
test-all: clean compile-all check coverage

coverage:
./gradlew jacocoTestReport
open plugins/nf-quilt/build/reports/jacoco/test/html/index.html

verifyCoverage:
./gradlew jacocoTestCoverageVerification

groovysh:
./gradlew -q --no-daemon --console=plain --init-script groovysh-task.gradle groovysh

#
# Create packages
Expand Down
49 changes: 49 additions & 0 deletions gradle-groovysh-init.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
gradle.projectsLoaded {
rootProject {
afterEvaluate { project ->
if (!project.repositories.any{it.name == 'MavenRepo'}) {
project.repositories {
// To be able to load org.codehaus.groovy:groovy-groovysh
mavenCentral()
}
}

project.configurations {
groovyshdependencies
}

project.dependencies {
groovyshdependencies("org.codehaus.groovy:groovy-groovysh:${GroovySystem.version}") {
exclude group: 'org.codehaus.groovy'
}
}

project.tasks.register('groovysh') {
group 'debug'
description 'Runs an interactive shell in the context of the project.'
doLast {
URLClassLoader groovyObjectClassLoader = GroovyObject.class.classLoader
def groovyshClass
def groovyShell

// Add dependency jars to classloader
configurations.groovyshdependencies.each {File file ->
groovyObjectClassLoader.addURL(file.toURL())
}
Class.forName('jline.console.history.FileHistory', true, groovyObjectClassLoader)
groovyshClass = Class.forName('org.codehaus.groovy.tools.shell.Groovysh', true, groovyObjectClassLoader)

if (groovyshClass) {
groovyShell = groovyshClass.newInstance()
}
if (groovyShell) {
groovyShell.interp.context.variables.put("gradle", gradle)
groovyShell.interp.context.variables.put("settings", gradle.settings)
groovyShell.interp.context.variables.put("project", project)
groovyShell.run('')
}
}
}
}
}
}
54 changes: 54 additions & 0 deletions groovysh-task.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
gradle.projectsLoaded {
rootProject {
afterEvaluate { project ->
if (!project.repositories.any{it.name == 'MavenRepo'}) {
project.repositories {
// To be able to load org.apache.groovy:groovy-groovysh and dependencies
mavenCentral {
content {
includeGroup 'org.apache.groovy'
includeGroup 'jline'
includeGroup 'com.github.javaparser'
includeGroup 'org.ow2.asm'
includeGroup 'org.abego.treelayout'
includeGroup 'org.apache.ivy'
}
}
}
}
project.configurations {
groovyshdependencies
}

project.dependencies {
groovyshdependencies "org.apache.groovy:groovy-groovysh:4.0.13"
}

project.tasks.register('groovysh') {
group 'debug'
description 'Runs an interactive shell in the context of the project. Use :inspect command to inspect project, gradle, settings or other objects.'
doLast {
URLClassLoader groovyshClassLoader = new URLClassLoader();
configurations.groovyshdependencies.each {File file ->
groovyshClassLoader.addURL(file.toURI().toURL())
}

def fileHistoryClass
def groovyshClass
def groovyShell
fileHistoryClass = Class.forName('jline.console.history.FileHistory', true, groovyshClassLoader)
groovyshClass = Class.forName('org.apache.groovy.groovysh.Groovysh', true, groovyshClassLoader)
if (groovyshClass) {
groovyShell = groovyshClass.newInstance()
if (groovyShell) {
groovyShell.interp.context.variables.put("gradle", gradle)
groovyShell.interp.context.variables.put("settings", gradle.settings)
groovyShell.interp.context.variables.put("project", project)
groovyShell.run('# Available objects: gradle, settings, project\n# Try :inspect project')
}
}
}
}
}
}
}
27 changes: 27 additions & 0 deletions plugins/nf-quilt/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ plugins {
id 'idea'
id 'se.patrikerdes.use-latest-versions' version '0.2.18'
id 'com.github.ben-manes.versions' version '0.51.0'
id 'jacoco'
}

useLatestVersions {
Expand Down Expand Up @@ -115,3 +116,29 @@ test {
useJUnitPlatform()
}

jacocoTestReport {
dependsOn test // tests are required to run before generating the report
}

jacocoTestCoverageVerification {
dependsOn jacocoTestReport // tests are required to run before generating the report
violationRules {
rule {
limit {
minimum = 0.65
}
}

rule {
enabled = false
element = 'CLASS'
includes = ['org.gradle.*']

limit {
counter = 'LINE'
value = 'TOTALCOUNT'
maximum = 0.3
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr
log.debug "QuiltFileSystemProvider.download: ${remoteFile} -> ${localDestination}"
QuiltPath qPath = asQuiltPath(remoteFile)
Path cachedFile = qPath.localPath()
/*
* UNUSED: QuiltPackage is always installed
*
QuiltPackage pkg = qPath.pkg()
if (!pkg.installed) {
log.info "download.install Quilt package: ${pkg}"
Expand All @@ -124,6 +127,7 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr
}
log.info "download.installed Quilt package to: $dest"
}
*/

if (!Files.exists(cachedFile)) {
log.error "download: File ${cachedFile} not found"
Expand Down Expand Up @@ -164,6 +168,9 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr
if (Files.exists(cachedFile)) {
throw new FileAlreadyExistsException(remoteDestination.toString())
}
if (!Files.exists(localFile)) {
throw new NoSuchFileException(localFile.toString())
}
Files.copy(localFile, cachedFile, options)
}

Expand Down Expand Up @@ -418,9 +425,9 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr

@Override
void copy(Path from, Path to, CopyOption... options) throws IOException {
// log.debug("Attempting `copy`: ${from} -> ${to}")
log.debug("Attempting `copy`: ${from} -> ${to}")
assert provider(from) == provider(to)
if (from == to) {
if (from.toString() == to.toString()) {
return // nothing to do -- just return
}

Expand Down
52 changes: 52 additions & 0 deletions plugins/nf-quilt/src/test/nextflow/quilt/QuiltObserverTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import nextflow.Session
import java.nio.file.Path
import java.nio.file.Paths
import groovy.transform.CompileDynamic
import spock.lang.Ignore

/**
*
Expand Down Expand Up @@ -95,4 +96,55 @@ class QuiltObserverTest extends QuiltSpecification {
'/bucket/file.ext' | 'quilt+s3://bucket#package=default_prefix%2fdefault_suffix&path=file.ext'
}

void 'should recover URI from onFilePublish QuiltPath'() {
given:
QuiltObserver observer = new QuiltObserver()
Path path = Paths.get(key)
Path quiltPath = QuiltPathFactory.parse(quilt_uri)
observer.onFilePublish(quiltPath, path)
String pkgKey = observer.pkgKey(quiltPath)
expect:
pkgKey == key
observer.uniqueURIs[key] == quilt_uri
observer.publishedURIs[key] == quilt_uri
where:
key | quilt_uri
'bucket/prefix/suffix' | 'quilt+s3://bucket#package=prefix%2fsuffix'
}

@Ignore('FIXME: handle onFilePublish with local Path')
void 'should extract URI from onFilePublish local Path'() {
given:
QuiltObserver observer = new QuiltObserver()
Path quiltPath = QuiltPathFactory.parse(quilt_uri)
Path path = Paths.get('/'+key)
observer.onFilePublish(path, quiltPath)
String pkgKey = observer.pkgKey(quiltPath)
expect:
pkgKey == key
observer.uniqueURIs[key] == quilt_uri
observer.publishedURIs[key] == quilt_uri
where:
key | quilt_uri
'bucket/prefix/suffix' | 'quilt+s3://bucket#package=prefix%2fsuffix'
}

void 'should not error on onFlowComplete'() {
given:
String quilt_uri = 'quilt+s3://bucket#package=prefix%2fsuffix'
QuiltObserver observer = new QuiltObserver()
QuiltPath qPath = QuiltPathFactory.parse(quilt_uri)
Session session = GroovyMock(Session) {
// getWorkflowMetadata() >> metadata
getParams() >> [outdir: quilt_uri]
isSuccess() >> false
}
observer.onFlowCreate(session)
observer.onFilePublish(qPath, qPath)
when:
observer.onFlowComplete()
then:
true
}

}
71 changes: 67 additions & 4 deletions plugins/nf-quilt/src/test/nextflow/quilt/QuiltProductTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package nextflow.quilt.nio

import nextflow.Session
import nextflow.script.WorkflowMetadata

import nextflow.quilt.QuiltSpecification
import nextflow.quilt.QuiltProduct
import nextflow.quilt.jep.QuiltParser
Expand All @@ -37,9 +39,16 @@ import spock.lang.Unroll
@CompileDynamic
class QuiltProductTest extends QuiltSpecification {

QuiltProduct makeProduct(String query=null) {
QuiltProduct makeProduct(String query=null, boolean success = false) {
String subURL = query ? fullURL.replace('key=val&key2=val2', query) : fullURL
Session session = Mock(Session)
WorkflowMetadata metadata = GroovyMock(WorkflowMetadata) {
toMap() >> [start:'2022-01-01', complete:'2022-01-02']
}
Session session = GroovyMock(Session) {
getWorkflowMetadata() >> metadata
getParams() >> [outdir: subURL]
isSuccess() >> success
}
QuiltPath path = QuiltPathFactory.parse(subURL)
return new QuiltProduct(path, session)
}
Expand All @@ -50,12 +59,23 @@ class QuiltProductTest extends QuiltSpecification {
String query = QuiltParser.unparseQuery(meta)
subURL = subURL.replace('#', "?${query}#")
}
Session session = Mock(Session)
Session session = GroovyMock(Session)
QuiltPath path = QuiltPathFactory.parse(subURL)
return new QuiltProduct(path, session)
}

void 'now should generate solid string for timestamp'() {
void 'should generate mocks from makeProduct'() {
given:
QuiltProduct product = makeProduct()

expect:
product
product.pkg
product.session != null
product.session.getWorkflowMetadata() != null
}

void 'should generate solid string for timestamp from now'() {
when:
def now = QuiltProduct.now()
then:
Expand Down Expand Up @@ -216,4 +236,47 @@ class QuiltProductTest extends QuiltSpecification {
Files.exists(Paths.get(sumPkg.packageDest().toString(), QuiltProduct.SUMMARY_FILE))
}

void 'should getMetadata from Map'() {
given:
Map meta = [
'Name': 'QuiltPackageTest',
'Owner': 'Ernest',
'Date': '1967-10-08',
'Type': 'NGS'
]
QuiltProduct product = makeProduct()
Map quilt_meta = product.getMetadata(meta)

expect:
quilt_meta != null
}

void 'should setupMeta from session'() {
given:
QuiltProduct product = makeProduct()
Map quilt_meta = product.setupMeta()

expect:
quilt_meta != null
}

void 'should throw error on publish'() {
given:
QuiltProduct product = makeProduct()

when:
product.publish()

then:
thrown(RuntimeException)
}

void 'should throw error if session.isSuccess'() {
when:
makeProduct(query: null, success: true)

then:
thrown(RuntimeException)
}

}
Loading
Loading