Skip to content

Commit

Permalink
feat: 42 audit get automated code coverage working (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
drernie authored Sep 4, 2024
1 parent 9831d5a commit e3fa9f5
Show file tree
Hide file tree
Showing 11 changed files with 379 additions and 15 deletions.
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

0 comments on commit e3fa9f5

Please sign in to comment.