Testing various buildscript dsl delegates; CollectionProviders now have contain/isCase methods.

This commit is contained in:
JesseBrault0709 2023-04-28 09:14:26 +02:00
parent 334fa655dd
commit ced050b793
12 changed files with 277 additions and 25 deletions

View File

@ -22,7 +22,8 @@ abstract class AbstractBuildDelegate<T> {
this.siteSpecClosures.inject(SiteSpec.getBlank()) { acc, closure -> this.siteSpecClosures.inject(SiteSpec.getBlank()) { acc, closure ->
def d = new SiteSpecDelegate() def d = new SiteSpecDelegate()
closure.delegate = d closure.delegate = d
closure.resolveStrategy = DELEGATE_FIRST //noinspection UnnecessaryQualifiedReference
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure() closure()
acc + d.getResult() acc + d.getResult()
} }
@ -32,7 +33,8 @@ abstract class AbstractBuildDelegate<T> {
this.globalsClosures.inject([:] as Map<String, Object>) { acc, closure -> this.globalsClosures.inject([:] as Map<String, Object>) { acc, closure ->
def d = new GlobalsDelegate() def d = new GlobalsDelegate()
closure.delegate = d closure.delegate = d
closure.resolveStrategy = DELEGATE_FIRST //noinspection UnnecessaryQualifiedReference
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure() closure()
acc + d.getResult() acc + d.getResult()
} }
@ -42,7 +44,8 @@ abstract class AbstractBuildDelegate<T> {
this.typesClosures.inject(TypesContainer.getEmpty()) { acc, closure -> this.typesClosures.inject(TypesContainer.getEmpty()) { acc, closure ->
def d = new TypesDelegate() def d = new TypesDelegate()
closure.delegate = d closure.delegate = d
closure.resolveStrategy = DELEGATE_FIRST //noinspection UnnecessaryQualifiedReference
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure() closure()
acc + d.getResult() acc + d.getResult()
} }
@ -52,7 +55,8 @@ abstract class AbstractBuildDelegate<T> {
this.sourcesClosures.inject(SourceProviders.getEmpty()) { acc, closure -> this.sourcesClosures.inject(SourceProviders.getEmpty()) { acc, closure ->
def d = new SourceProvidersDelegate() def d = new SourceProvidersDelegate()
closure.delegate = d closure.delegate = d
closure.resolveStrategy = DELEGATE_FIRST //noinspection UnnecessaryQualifiedReference
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure(typesContainer) closure(typesContainer)
acc + d.getResult() acc + d.getResult()
} }
@ -62,7 +66,8 @@ abstract class AbstractBuildDelegate<T> {
this.taskFactoriesClosures.inject([:] as Map<String, TaskFactorySpec>) { acc, closure -> this.taskFactoriesClosures.inject([:] as Map<String, TaskFactorySpec>) { acc, closure ->
def d = new TaskFactoriesDelegate() def d = new TaskFactoriesDelegate()
closure.delegate = d closure.delegate = d
closure.resolveStrategy = DELEGATE_FIRST //noinspection UnnecessaryQualifiedReference
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure(sourceProviders) closure(sourceProviders)
def specs = d.getResult() def specs = d.getResult()
specs.forEach { name, spec -> specs.forEach { name, spec ->

View File

@ -30,7 +30,11 @@ final class TaskFactoriesDelegate {
this.specs[name] = new TaskFactorySpec<>(factorySupplier, [factoryConfigurator]) this.specs[name] = new TaskFactorySpec<>(factorySupplier, [factoryConfigurator])
} }
def <T extends TaskFactory> void configure(String name, Class<T> factoryClass, Consumer<T> factoryConfigureClosure) { def <T extends TaskFactory> void configure(
String name,
Class<T> factoryClass, // Dummy so we get better auto-complete
Consumer<T> factoryConfigureClosure
) {
if (!this.specs.containsKey(name)) { if (!this.specs.containsKey(name)) {
throw new IllegalArgumentException("there is no TaskFactory registered by name ${ name }") throw new IllegalArgumentException("there is no TaskFactory registered by name ${ name }")
} }

View File

@ -1,19 +1,48 @@
package com.jessebrault.ssg.provider package com.jessebrault.ssg.provider
import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck
import groovy.transform.TupleConstructor
@TupleConstructor(defaults = false, includeFields = true)
@NullCheck(includeGenerated = true)
@EqualsAndHashCode(includeFields = true)
abstract class AbstractCollectionProvider<T> implements CollectionProvider<T> { abstract class AbstractCollectionProvider<T> implements CollectionProvider<T> {
static <T> CollectionProvider<T> concat(
CollectionProvider<T> cp,
Provider<T> p
) {
new SupplierBasedCollectionProvider<>([cp], [p], {
[*cp.provide(), p.provide()]
})
}
static <T> CollectionProvider<T> concat( static <T> CollectionProvider<T> concat(
CollectionProvider<T> cp0, CollectionProvider<T> cp0,
CollectionProvider<T> cp1 CollectionProvider<T> cp1
) { ) {
SupplierBasedCollectionProvider.get { new SupplierBasedCollectionProvider<>([cp0, cp1], [], {
cp0.provide() + cp1.provide() cp0.provide() + cp1.provide()
})
} }
private final Collection<CollectionProvider<T>> collectionProviderChildren
private final Collection<Provider<T>> providerChildren
@Override
boolean contains(Provider<T> provider) {
provider in this
}
@Override
boolean contains(CollectionProvider<T> collectionProvider) {
collectionProvider in this
} }
@Override @Override
CollectionProvider<T> plus(Provider<T> other) { CollectionProvider<T> plus(Provider<T> other) {
concat(this, other as CollectionProvider<T>) concat(this, other)
} }
@Override @Override
@ -21,4 +50,23 @@ abstract class AbstractCollectionProvider<T> implements CollectionProvider<T> {
concat(this, other) concat(this, other)
} }
@Override
boolean isCase(Provider<T> provider) {
provider in this.providerChildren || this.providerChildren.inject(false) { acc, childProvider ->
acc || provider in childProvider
}
}
@Override
boolean isCase(CollectionProvider<T> collectionProvider) {
collectionProvider == this
|| collectionProvider in this.collectionProviderChildren
|| this.collectionProviderChildren.inject(
false,
{ acc, childCollectionProvider ->
acc || collectionProvider in childCollectionProvider
}
)
}
} }

View File

@ -6,9 +6,9 @@ abstract class AbstractProvider<T> implements Provider<T> {
Provider<T> p0, Provider<T> p0,
Provider<T> p1 Provider<T> p1
) { ) {
SupplierBasedCollectionProvider.get { new SupplierBasedCollectionProvider<>({
[p0.provide(), p1.provide()] [p0.provide(), p1.provide()]
} })
} }
@Override @Override
@ -18,9 +18,9 @@ abstract class AbstractProvider<T> implements Provider<T> {
@Override @Override
CollectionProvider<T> asType(Class<CollectionProvider> collectionProviderClass) { CollectionProvider<T> asType(Class<CollectionProvider> collectionProviderClass) {
SupplierBasedCollectionProvider.get { new SupplierBasedCollectionProvider<>({
[this.provide() as T] [this.provide() as T]
} })
} }
} }

View File

@ -2,6 +2,13 @@ package com.jessebrault.ssg.provider
interface CollectionProvider<T> { interface CollectionProvider<T> {
Collection<T> provide() Collection<T> provide()
boolean contains(Provider<T> provider)
boolean contains(CollectionProvider<T> collectionProvider)
CollectionProvider<T> plus(Provider<T> other) CollectionProvider<T> plus(Provider<T> other)
CollectionProvider<T> plus(CollectionProvider<T> other) CollectionProvider<T> plus(CollectionProvider<T> other)
boolean isCase(Provider<T> provider)
boolean isCase(CollectionProvider<T> collectionProvider)
} }

View File

@ -5,7 +5,6 @@ import groovy.io.FileType
import groovy.transform.EqualsAndHashCode import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck import groovy.transform.NullCheck
import groovy.transform.PackageScope import groovy.transform.PackageScope
import groovy.transform.TupleConstructor
import org.jetbrains.annotations.Nullable import org.jetbrains.annotations.Nullable
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -13,9 +12,8 @@ import org.slf4j.LoggerFactory
import java.util.function.BiFunction import java.util.function.BiFunction
@PackageScope @PackageScope
@TupleConstructor(includeFields = true, defaults = false) @NullCheck
@NullCheck(includeGenerated = true) @EqualsAndHashCode(includeFields = true, callSuper = true)
@EqualsAndHashCode(includeFields = true)
final class FileBasedCollectionProvider<T> extends AbstractCollectionProvider<T> { final class FileBasedCollectionProvider<T> extends AbstractCollectionProvider<T> {
private static final Logger logger = LoggerFactory.getLogger(FileBasedCollectionProvider) private static final Logger logger = LoggerFactory.getLogger(FileBasedCollectionProvider)
@ -23,6 +21,12 @@ final class FileBasedCollectionProvider<T> extends AbstractCollectionProvider<T>
private final File baseDirectory private final File baseDirectory
private final BiFunction<File, String, @Nullable T> elementFunction private final BiFunction<File, String, @Nullable T> elementFunction
FileBasedCollectionProvider(File baseDirectory, BiFunction<File, String, T> elementFunction) {
super([], [])
this.baseDirectory = baseDirectory
this.elementFunction = elementFunction
}
@Override @Override
Collection<T> provide() { Collection<T> provide() {
if (!this.baseDirectory.isDirectory()) { if (!this.baseDirectory.isDirectory()) {

View File

@ -3,16 +3,19 @@ package com.jessebrault.ssg.provider
import groovy.transform.EqualsAndHashCode import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck import groovy.transform.NullCheck
import groovy.transform.PackageScope import groovy.transform.PackageScope
import groovy.transform.TupleConstructor
@PackageScope @PackageScope
@TupleConstructor(defaults = false, includeFields = true) @NullCheck
@NullCheck(includeGenerated = true) @EqualsAndHashCode(includeFields = true, callSuper = true)
@EqualsAndHashCode(includeFields = true)
final class SimpleCollectionProvider<T> extends AbstractCollectionProvider<T> { final class SimpleCollectionProvider<T> extends AbstractCollectionProvider<T> {
private final Collection<T> ts private final Collection<T> ts
SimpleCollectionProvider(Collection<T> ts) {
super([], [])
this.ts = ts
}
@Override @Override
Collection<T> provide() { Collection<T> provide() {
this.ts this.ts

View File

@ -3,18 +3,29 @@ package com.jessebrault.ssg.provider
import groovy.transform.EqualsAndHashCode import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck import groovy.transform.NullCheck
import groovy.transform.PackageScope import groovy.transform.PackageScope
import groovy.transform.TupleConstructor
import java.util.function.Supplier import java.util.function.Supplier
@PackageScope @PackageScope
@TupleConstructor(defaults = false, includeFields = true) @NullCheck
@NullCheck(includeGenerated = true) @EqualsAndHashCode(includeFields = true, callSuper = true)
@EqualsAndHashCode(includeFields = true)
final class SupplierBasedCollectionProvider<T> extends AbstractCollectionProvider<T> { final class SupplierBasedCollectionProvider<T> extends AbstractCollectionProvider<T> {
private final Supplier<Collection<T>> supplier private final Supplier<Collection<T>> supplier
SupplierBasedCollectionProvider(
Collection<CollectionProvider<T>> collectionProviderChildren,
Collection<Provider<T>> providerChildren,
Supplier<Collection<T>> supplier
) {
super(collectionProviderChildren, providerChildren)
this.supplier = supplier
}
SupplierBasedCollectionProvider(Supplier<Collection<T>> supplier) {
this([], [], supplier)
}
@Override @Override
Collection<T> provide() { Collection<T> provide() {
this.supplier.get() this.supplier.get()

View File

@ -0,0 +1,97 @@
package com.jessebrault.ssg.buildscript.dsl
import com.jessebrault.ssg.SiteSpec
import com.jessebrault.ssg.buildscript.SourceProviders
import com.jessebrault.ssg.buildscript.TypesContainer
import com.jessebrault.ssg.page.Page
import com.jessebrault.ssg.page.PageTypes
import com.jessebrault.ssg.provider.CollectionProvider
import com.jessebrault.ssg.task.TaskFactory
import com.jessebrault.ssg.text.Text
import com.jessebrault.ssg.text.TextTypes
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import java.util.function.Supplier
import static org.junit.jupiter.api.Assertions.assertEquals
import static org.junit.jupiter.api.Assertions.assertTrue
@ExtendWith(MockitoExtension)
abstract class AbstractBuildDelegateTests<T> {
protected abstract AbstractBuildDelegate<T> getDelegate()
@Test
void siteSpecsAdded() {
def d = this.getDelegate()
d.siteSpec {
name = 'test'
}
d.siteSpec {
baseUrl = 'test'
}
def sum = d.getSiteSpecResult()
assertEquals(new SiteSpec('test', 'test'), sum)
}
@Test
void globalsAdded() {
def d = this.getDelegate()
d.globals {
a = 0
}
d.globals {
b = 1
}
def sum = d.getGlobalsResult()
assertEquals([a: 0, b: 1], sum)
}
@Test
void typesAdded() {
def d = this.getDelegate()
d.types {
textTypes << TextTypes.MARKDOWN
}
d.types {
pageTypes << PageTypes.GSP
}
def sum = d.getTypesResult()
assertTrue(TextTypes.MARKDOWN in sum.textTypes)
assertTrue(PageTypes.GSP in sum.pageTypes)
}
@Test
void sourcesAdded(@Mock CollectionProvider<Text> textsProvider, @Mock CollectionProvider<Page> pagesProvider) {
def d = this.getDelegate()
d.providers {
texts(textsProvider)
}
d.providers {
pages(pagesProvider)
}
def sum = d.getSourcesResult(TypesContainer.getEmpty())
assertTrue(textsProvider in sum.textsProvider)
assertTrue(pagesProvider in sum.pagesProvider)
}
@Test
void taskFactoriesAdded(@Mock Supplier<TaskFactory> taskFactorySupplier) {
def d = this.getDelegate()
d.taskFactories {
register('tf0', taskFactorySupplier)
}
d.taskFactories {
register('tf1', taskFactorySupplier)
}
def sum = d.getTaskFactoriesResult(SourceProviders.getEmpty())
assertEquals(2, sum.size())
assertTrue(sum.inject(true) { acc, spec ->
acc && spec.supplier == taskFactorySupplier
})
}
}

View File

@ -0,0 +1,12 @@
package com.jessebrault.ssg.buildscript.dsl
import com.jessebrault.ssg.buildscript.Build
final class AllBuildsDelegateTests extends AbstractBuildDelegateTests<Build.AllBuilds> {
@Override
protected AbstractBuildDelegate<Build.AllBuilds> getDelegate() {
new AllBuildsDelegate()
}
}

View File

@ -0,0 +1,12 @@
package com.jessebrault.ssg.buildscript.dsl
import com.jessebrault.ssg.buildscript.Build
final class BuildDelegateTests extends AbstractBuildDelegateTests<Build> {
@Override
protected AbstractBuildDelegate<Build> getDelegate() {
new BuildDelegate()
}
}

View File

@ -0,0 +1,49 @@
package com.jessebrault.ssg.buildscript.dsl
import com.jessebrault.ssg.task.TaskFactory
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.function.Executable
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import java.util.function.Consumer
import java.util.function.Supplier
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow
import static org.junit.jupiter.api.Assertions.assertEquals
import static org.junit.jupiter.api.Assertions.assertTrue
@ExtendWith(MockitoExtension)
final class TaskFactoriesDelegateTests {
@Test
void registerThenConfigure(
@Mock Supplier<TaskFactory> taskFactorySupplier,
@Mock Consumer<TaskFactory> taskFactoryConsumer
) {
def d = new TaskFactoriesDelegate()
d.register('test', taskFactorySupplier)
assertDoesNotThrow({
d.configure('test', TaskFactory, taskFactoryConsumer)
} as Executable)
def result = d.getResult()
assertTrue(result.containsKey('test'))
assertEquals(taskFactorySupplier, result['test'].supplier)
assertTrue(result['test'].configurators.contains(taskFactoryConsumer))
}
@Test
void registerAndConfigure(
@Mock Supplier<TaskFactory> taskFactorySupplier,
@Mock Consumer<TaskFactory> taskFactoryConsumer
) {
def d = new TaskFactoriesDelegate()
d.register('test', taskFactorySupplier, taskFactoryConsumer)
def result = d.getResult()
assertTrue(result.containsKey('test'))
assertEquals(taskFactorySupplier, result['test'].supplier)
assertTrue(result['test'].configurators.contains(taskFactoryConsumer))
}
}