Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4d1d018
created stub and test
franciscolopezsancho Sep 24, 2021
99c870b
first test passing
franciscolopezsancho Sep 26, 2021
f5cd830
following suggestions
franciscolopezsancho Sep 27, 2021
ff352a7
following review suggestion
franciscolopezsancho Sep 27, 2021
0010b1c
added ActionTestkitGenerator
franciscolopezsancho Sep 28, 2021
d0edaef
changing order expected,actualValue
franciscolopezsancho Sep 28, 2021
df0528a
Codegen already functional
franciscolopezsancho Oct 4, 2021
28a9b8a
Added test example generation
franciscolopezsancho Oct 4, 2021
f56765a
Merge branch 'main' of github.com:franciscolopezsancho/akkaserverless…
franciscolopezsancho Oct 4, 2021
91c01c0
adding docs
franciscolopezsancho Oct 4, 2021
742af2c
adding FIXME
franciscolopezsancho Oct 4, 2021
f240b9d
formatting
franciscolopezsancho Oct 5, 2021
1dd9b12
adding tag to docs
franciscolopezsancho Oct 5, 2021
b862130
Merge branch 'main' of github.com:franciscolopezsancho/akkaserverless…
franciscolopezsancho Oct 5, 2021
ee4f266
adding missing import
franciscolopezsancho Oct 5, 2021
e79b1c3
changing selectInput/Output from ModelBuilder.Commands
franciscolopezsancho Oct 5, 2021
75deb3d
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 5, 2021
e417a83
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 5, 2021
b221a9e
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 5, 2021
997678f
Update samples/java-eventsourced-counter/src/test/java/com/example/ac…
franciscolopezsancho Oct 5, 2021
1fa4764
adding codegen for RPCs streaming, client and server
franciscolopezsancho Oct 5, 2021
fb1ee72
adding comment after imports
franciscolopezsancho Oct 5, 2021
f4bb328
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
c65d83e
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
c0b2e8a
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
10a7894
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
eee315b
adding suggestions
franciscolopezsancho Oct 6, 2021
ba010e0
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
34524db
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -505,5 +505,4 @@ object ModelBuilder {
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* Copyright 2021 Lightbend Inc.
*
* 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.
*/

package com.lightbend.akkasls.codegen
package java

import com.google.common.base.Charsets
import _root_.java.nio.file.{ Files, Path }

object ActionTestKitGenerator {
import com.lightbend.akkasls.codegen.SourceGeneratorUtils._

def generate(
service: ModelBuilder.ActionService,
testSourceDirectory: Path,
generatedTestSourceDirectory: Path): Iterable[Path] = {
var generatedFiles: Seq[Path] = Vector.empty
val packageName = service.fqn.parent.javaPackage
val className = service.className

val packagePath = packageAsPath(packageName)
val testKitPath = generatedTestSourceDirectory.resolve(packagePath.resolve(className + "TestKit.java"))
testKitPath.getParent.toFile.mkdirs()
val sourceCode = generateSourceCode(service)
Files.write(testKitPath, sourceCode.getBytes(Charsets.UTF_8))
generatedFiles :+= testKitPath

val testFilePath = testSourceDirectory.resolve(packagePath.resolve(className + "Test.java"))
if (!testFilePath.toFile.exists()) {
testFilePath.getParent.toFile.mkdirs()
Files.write(testFilePath, generateTestSourceCode(service).getBytes(Charsets.UTF_8))
generatedFiles :+= testFilePath
}

generatedFiles
}

private[codegen] def generateSourceCode(service: ModelBuilder.ActionService): String = {

val packageName = service.fqn.parent.javaPackage
val className = service.className

val imports = generateImports(
commandTypes(service.commands),
"",
otherImports = Seq(
"java.util.ArrayList",
"java.util.List",
"java.util.function.Function",
"java.util.Optional",
s"$packageName.$className",
"com.akkaserverless.javasdk.action.Action",
"com.akkaserverless.javasdk.action.ActionCreationContext",
"com.akkaserverless.javasdk.testkit.ActionResult",
"com.akkaserverless.javasdk.testkit.impl.ActionResultImpl",
"com.akkaserverless.javasdk.impl.action.ActionEffectImpl",
"com.akkaserverless.javasdk.testkit.impl.StubActionCreationContext",
"com.akkaserverless.javasdk.testkit.impl.StubActionContext")
++ commandStreamedTypes(service.commands))

val testKitClassName = s"${className}TestKit"

s"""package ${service.fqn.parent.javaPackage};
|
|$imports
|
|$managedComment
|
|public final class $testKitClassName {
|
| private Function<ActionCreationContext, $className> actionFactory;
|
| private $className createAction() {
| $className action = actionFactory.apply(new StubActionCreationContext());
| action._internalSetActionContext(Optional.of(new StubActionContext()));
| return action;
| };
|
| public static $testKitClassName of(Function<ActionCreationContext, $className> actionFactory) {
| return new $testKitClassName(actionFactory);
| }
|
| private $testKitClassName(Function<ActionCreationContext, $className> actionFactory) {
| this.actionFactory = actionFactory;
| }
|
| private <E> ActionResult<E> interpretEffects(Action.Effect<E> effect) {
| return new ActionResultImpl(effect);
| }
|
| ${Format.indent(generateServices(service), 2)}
|
|}
|""".stripMargin
}

private[codegen] def generateTestSourceCode(service: ModelBuilder.ActionService): String = {
val className = service.className
val packageName = service.fqn.parent.javaPackage
val imports = generateImports(
commandTypes(service.commands),
"",
otherImports = Seq(
s"$packageName.$className",
s"${packageName}.${className}TestKit",
"com.akkaserverless.javasdk.testkit.ActionResult",
"org.junit.Test",
"static org.junit.Assert.*")
++ commandStreamedTypes(service.commands))

val testClassName = s"${className}Test"

s"""$unmanagedComment
|package ${service.fqn.parent.javaPackage};
|
|$imports
|
|public class $testClassName {
|
| @Test
| public void exampleTest() {
| ${className}TestKit testKit = ${className}TestKit.of($className::new);
| // use the testkit to execute a command
| // ActionResult<SomeResponse> result = testKit.someOperation(SomeRequest);
| // verify the response
| // SomeResponse actualResponse = result.getReply();
| // assertEquals(expectedResponse, actualResponse);
| }
|
| ${Format.indent(generateTestingServices(service), 2)}
|
|}
|""".stripMargin
}

def generateServices(service: ModelBuilder.ActionService): String = {
require(!service.commands.isEmpty, "empty `commands` not allowed")

service.commands
.map { command =>
s"""|public ${selectOutputResult(command)} ${lowerFirst(command.name)}(${selectInput(command)} ${lowerFirst(
command.inputType.protoName)}) {
| ${selectOutputEffect(command)} effect = createAction().${lowerFirst(command.name)}(${lowerFirst(
command.inputType.protoName)});
| return ${selectOutputReturn(command)}
|}
|""".stripMargin + "\n"
}
.mkString("")
}

def generateTestingServices(service: ModelBuilder.ActionService): String = {
service.commands
.map { command =>
s"""|@Test
|public void ${lowerFirst(command.name)}Test() {
| ${service.className}TestKit testKit = ${service.className}TestKit.of(${service.className}::new);
| // ${selectOutputResult(command)} result = testKit.${lowerFirst(command.name)}(${selectInput(command)}.newBuilder()...build());
|}
|""".stripMargin + "\n"
}
.mkString("")
}

def selectOutputResult(command: ModelBuilder.Command): String = {
if (command.streamedOutput)
s"Source<ActionResult<${command.outputType.fullName}>, akka.NotUsed>"
else s"ActionResult<${command.outputType.fullName}>"
}

def selectOutputEffect(command: ModelBuilder.Command): String = {
if (command.streamedOutput)
s"Source<Action.Effect<${command.outputType.fullName}>, akka.NotUsed>"
else s"Action.Effect<${command.outputType.fullName}>"
}

def selectOutputReturn(command: ModelBuilder.Command): String = {
if (command.streamedOutput) "effect.map(e -> interpretEffects(e));"
else "interpretEffects(effect);"
}

def selectInput(command: ModelBuilder.Command): String = {
if (command.streamedInput) s"Source<${command.inputType.fullName}, akka.NotUsed>"
else command.inputType.fullName
}

def commandStreamedTypes(commands: Iterable[ModelBuilder.Command]): Seq[String] = {
if (commands.exists(c => c.streamedInput || c.streamedOutput)) Seq("akka.stream.javadsl.Source", "akka.NotUsed")
else Nil
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ object SourceGenerator {
integrationTestSourceDirectory,
generatedSourceDirectory)
case service: ModelBuilder.ActionService =>
ActionServiceSourceGenerator.generate(service, sourceDirectory, generatedSourceDirectory)
ActionServiceSourceGenerator.generate(service, sourceDirectory, generatedSourceDirectory) ++
ActionTestKitGenerator.generate(service, testSourceDirectory, generatedTestSourceDirectory)
case _ => Seq.empty
} ++ {
MainSourceGenerator.generate(
Expand Down
Binary file not shown.
Loading