Skip to content

Commit

Permalink
Run JS API unit tests and integration tests in the Gradle build (deep…
Browse files Browse the repository at this point in the history
…haven#4988)

Restores some unit and integration tests to run as part of the
local/CI builds, as part of the check task.

Partial deephaven#188
  • Loading branch information
niloc132 authored Jan 17, 2024
1 parent 2120b16 commit e7af7a1
Show file tree
Hide file tree
Showing 21 changed files with 2,583 additions and 146 deletions.
30 changes: 9 additions & 21 deletions buildSrc/src/main/groovy/GwtTools.groovy
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import de.esoco.gwt.gradle.GwtLibPlugin
import de.esoco.gwt.gradle.GwtPlugin
import de.esoco.gwt.gradle.extension.GwtExtension
import de.esoco.gwt.gradle.task.GwtCheckTask
import de.esoco.gwt.gradle.task.GwtCompileTask
import groovy.transform.CompileStatic
import org.gradle.api.Project
Expand Down Expand Up @@ -36,6 +37,13 @@ class GwtTools {
GwtCompileTask gwtc ->
applyModuleSettings p, gwtc, module,description
}
// This GWT plugin will fail if tests are run after compilation, instead
// we suppress running the test at all, and ensure that it doesn't check
// if it even can be run until after compile finishes.
p.tasks.withType(GwtCheckTask).configureEach {t ->
t.mustRunAfter(p.tasks.withType(GwtCompileTask))
t.onlyIf { false }
}

return ext
}
Expand Down Expand Up @@ -70,8 +78,6 @@ class GwtTools {
gwtDev && gwtc.doFirst {
gwtc.logger.quiet('Running in gwt dev mode; saving source to {}/dh/src', extras)
}

p.tasks.findByName('gwtCheck')?.enabled = false
}

static void applyDefaults(Project p, GwtExtension gwt, boolean compile = false) {
Expand Down Expand Up @@ -110,31 +116,13 @@ class GwtTools {
maxHeapSize = "1024m"
minHeapSize = "512m"
}

gwt.dev.with {
/** The ip address of the code server. */
bindAddress = "127.0.0.1"
/** The port where the code server will run. */
port = 9876
/** Specifies Java source level ("1.6", "1.7").
sourceLevel = "1.8"
/** The level of logging detail (ERROR, WARN, INFO, TRACE, DEBUG, SPAM, ALL) */
logLevel = "INFO"
/** Emit extra information allow chrome dev tools to display Java identifiers in many placesinstead of JavaScript functions. (NONE, ONLY_METHOD_NAME, ABBREVIATED, FULL) */
methodNameDisplayMode = "NONE"
/** Where to write output files */
war = warPath
// extraArgs = ["-firstArgument", "-secondArgument"]
}


}
}

static void addGeneratedSources(Project project, GwtCompileTask gwtc) {
if (project.configurations.getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME).dependencies) {
(gwtc.src as ConfigurableFileCollection).from(
(project.tasks.getByName(JavaPlugin.COMPILE_JAVA_TASK_NAME) as JavaCompile).options.annotationProcessorGeneratedSourcesDirectory
(project.tasks.getByName(JavaPlugin.COMPILE_JAVA_TASK_NAME) as JavaCompile).options.generatedSourceOutputDirectory
)
}
project.configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).allDependencies.withType(ProjectDependency)*.dependencyProject*.each {
Expand Down
3 changes: 3 additions & 0 deletions docker/registry/selenium/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plugins {
id 'io.deephaven.project.register'
}
3 changes: 3 additions & 0 deletions docker/registry/selenium/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
io.deephaven.project.ProjectType=DOCKER_REGISTRY
deephaven.registry.imageName=selenium/standalone-firefox:4.16.1-20231219
deephaven.registry.imageId=selenium/standalone-firefox@sha256:a405fe92b3ce5d7eb31a07e1f99be3d628fdc0e5bdc81febd8dc11786edef024
33 changes: 0 additions & 33 deletions gradle/web-gwt-test.gradle

This file was deleted.

110 changes: 108 additions & 2 deletions web/client-api/client-api.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import com.bmuschko.gradle.docker.tasks.container.DockerCreateContainer
import com.bmuschko.gradle.docker.tasks.container.DockerRemoveContainer
import com.bmuschko.gradle.docker.tasks.container.DockerStartContainer
import io.deephaven.tools.docker.WaitForHealthyContainer

plugins {
id 'io.deephaven.project.register'
id 'io.deephaven.deephaven-in-docker'
}

evaluationDependsOn(Docker.registryProject('selenium'))

apply from: "$rootDir/gradle/web-client.gradle"

configurations {
js
dts
typescriptDoclet
testImplementation.extendsFrom junit
}

dependencies {
Expand All @@ -20,6 +29,8 @@ dependencies {
implementation 'com.vertispan.nio:gwt-nio:1.0-alpha-1'

js project(path: ':proto:raw-js-openapi', configuration: 'js')

testImplementation 'org.seleniumhq.selenium:selenium-remote-driver:4.16.1'
}
Classpaths.inheritElemental(project, 'elemental2-core', 'implementation')
Classpaths.inheritElemental(project, 'elemental2-promise', 'implementation')
Expand Down Expand Up @@ -57,6 +68,101 @@ artifacts {
}
}

project.tasks.getByName('quick').dependsOn project.tasks.withType(de.esoco.gwt.gradle.task.GwtCompileTask)
def gwtUnitTest = tasks.register('gwtUnitTest', Test) { t ->
t.systemProperties = [
'gwt.args': ['-runStyle HtmlUnit',
'-ea',
'-style PRETTY',
"-war ${layout.buildDirectory.dir('unitTest-war').get().asFile.absolutePath}"
].join(' '),
'gwt.persistentunitcachedir': layout.buildDirectory.dir('unitTest-unitCache').get().asFile.absolutePath,
]
t.include '**/ClientUnitTestSuite.class'
t.useJUnit()
t.scanForTestClasses = false
}

// start a grpc-api server
String randomSuffix = UUID.randomUUID().toString();
deephavenDocker {
envVars.set([
'START_OPTS':'-Xmx512m -DAuthHandlers=io.deephaven.auth.AnonymousAuthenticationHandler'
])
containerName.set "dh-server-for-js-${randomSuffix}"
networkName.set "js-test-network-${randomSuffix}"
}

def seleniumContainerId = "selenium-${randomSuffix}"
def seleniumPort
if (!hasProperty('selenium.port')) {
seleniumPort = '4444'
} else {
seleniumPort = project.getProperty('selenium.port')
}

def createSelenium = tasks.register('createSelenium', DockerCreateContainer) { t ->
t.dependsOn(Docker.registryTask(project, 'selenium'), deephavenDocker.startTask)
t.targetImageId('deephaven/selenium:local-build')
t.containerName.set(seleniumContainerId)
// Advised by the selenium documentation
t.hostConfig.shmSize.set(2L * 1024 * 1024 * 1024)

apply from: "$rootDir/gradle/web-gwt-test.gradle"
// Add our own healthcheck to confirm the container starts fully
t.healthCheck.cmd.set(['curl http://localhost:4444/wd/hub/status || exit 1'])

// This provides a hostname that can be referenced from inside the docker container to access the host
// OS, and connect to the test server.
t.hostConfig.extraHosts.add('host.docker.internal:host-gateway')
t.hostConfig.portBindings.set(["$seleniumPort:4444"])
t.hostConfig.network.set(deephavenDocker.networkName.get())
}
def startSelenium = tasks.register('startSelenium', DockerStartContainer) {t ->
t.dependsOn(createSelenium)
t.containerId.set(seleniumContainerId)
}
def seleniumHealthy = project.tasks.register('seleniumHealthy', WaitForHealthyContainer) { task ->
task.dependsOn startSelenium

task.awaitStatusTimeout.set 120
task.checkInterval.set 100

task.containerId.set(seleniumContainerId)
}
def stopSelenium = project.tasks.register('stopSelenium', DockerRemoveContainer) { task ->
task.dependsOn startSelenium
task.targetContainerId seleniumContainerId
task.force.set true
task.removeVolumes.set true
}

def gwtIntegrationTest = tasks.register('gwtIntegrationTest', Test) { t ->
t.dependsOn(deephavenDocker.portTask, seleniumHealthy)
t.finalizedBy(deephavenDocker.endTask, stopSelenium)
doFirst {
def webdriverUrl = "http://localhost:${seleniumPort}/"
t.systemProperty('gwt.args', ["-runStyle io.deephaven.web.junit.RunStyleRemoteWebDriver:${webdriverUrl}?firefox",
'-ea',
'-style PRETTY',
"-setProperty dh.server=http://${deephavenDocker.containerName.get()}:10000",
"-war ${layout.buildDirectory.dir('integrationTest-war').get().asFile.absolutePath}"
].join(' '))
t.classpath += tasks.getByName('gwtCompile').src
}
t.finalizedBy(deephavenDocker.endTask)
t.systemProperties = [
'gwt.persistentunitcachedir':layout.buildDirectory.dir('integrationTest-unitCache').get().asFile.absolutePath,
'webdriver.test.host':'host.docker.internal',
]
t.include '**/ClientIntegrationTestSuite.class'
t.useJUnit()
t.scanForTestClasses = false
}

tasks.named('check').configure {
dependsOn(gwtUnitTest, gwtIntegrationTest)
}

test {
// Configure jvm-only tests to not run any GWT-only tests
exclude '**/*TestGwt.class', '**/*TestSuite.class'
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import io.deephaven.web.client.api.batch.TableConfig;
import io.deephaven.web.client.api.console.JsVariableType;
import io.deephaven.web.client.api.filter.FilterCondition;
import io.deephaven.web.client.api.filter.FilterValue;
import io.deephaven.web.client.api.input.JsInputTable;
import io.deephaven.web.client.api.lifecycle.HasLifecycle;
import io.deephaven.web.client.api.state.StateCache;
Expand All @@ -71,6 +72,8 @@
import io.deephaven.web.shared.fu.JsProvider;
import io.deephaven.web.shared.fu.JsRunnable;
import io.deephaven.web.shared.fu.RemoverFn;
import javaemul.internal.annotations.DoNotAutobox;
import jsinterop.annotations.JsIgnore;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsNullable;
import jsinterop.annotations.JsOptional;
Expand Down Expand Up @@ -594,6 +597,11 @@ public JsArray<FilterCondition> applyFilter(FilterCondition[] filter) {
@TsUnion
@JsType(name = "?", namespace = JsPackage.GLOBAL, isNative = true)
public interface CustomColumnArgUnionType {
@JsOverlay
static CustomColumnArgUnionType of(@DoNotAutobox Object value) {
return Js.cast(value);
}

@JsOverlay
default boolean isString() {
return (Object) this instanceof String;
Expand Down Expand Up @@ -1762,8 +1770,8 @@ public void handleSnapshot(TableTicket handle, TableSnapshot snapshot) {
viewportRows.size());
}


protected void processSnapshot() {
@JsIgnore
public void processSnapshot() {
try {
if (debounce == null) {
JsLog.debug("Skipping snapshot b/c debounce is null");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.deephaven.web;

import com.google.gwt.junit.tools.GWTTestSuite;
import io.deephaven.web.client.api.NullValueTestGwt;
import io.deephaven.web.client.api.subscription.ConcurrentTableTestGwt;
import io.deephaven.web.client.api.TableManipulationTestGwt;
import io.deephaven.web.client.api.subscription.ViewportTestGwt;
import junit.framework.Test;
import junit.framework.TestSuite;

public class ClientIntegrationTestSuite extends GWTTestSuite {
public static Test suite() {
TestSuite suite = new TestSuite("Deephaven JS API Integration Test Suite");

// This test doesn't actually talk to the server, but it requires the dh-internal library be available.
// Disabled for now, we don't have good toString on the FilterCondition/FilterValue types.
// suite.addTestSuite(FilterConditionTestGwt.class);

// Actual integration tests
suite.addTestSuite(ViewportTestGwt.class);
suite.addTestSuite(TableManipulationTestGwt.class);
suite.addTestSuite(ConcurrentTableTestGwt.class);
suite.addTestSuite(NullValueTestGwt.class);

// Unfinished:
// suite.addTestSuite(TotalsTableTestGwt.class);

return suite;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
package io.deephaven.web;

import com.google.gwt.junit.tools.GWTTestSuite;
import io.deephaven.web.client.api.filter.FilterConditionTestGwt;
import io.deephaven.web.client.api.i18n.JsDateTimeFormatTestGwt;
import io.deephaven.web.client.api.i18n.JsNumberFormatTestGwt;
import junit.framework.Test;
import junit.framework.TestSuite;

public class ApiTestSuite extends GWTTestSuite {
/**
* Tests that require a browser environment to run, but do not require the server.
*/
public class ClientUnitTestSuite extends GWTTestSuite {
public static Test suite() {
TestSuite suite = new TestSuite("Deephaven Web API Test Suite");
suite.addTestSuite(FilterConditionTestGwt.class);

TestSuite suite = new TestSuite("Deephaven JS API Unit Test Suite");
suite.addTestSuite(JsDateTimeFormatTestGwt.class);
suite.addTestSuite(JsNumberFormatTestGwt.class);
return suite;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<module>
<inherits name="io.deephaven.web.DeephavenApi" />

<!-- restore the default linker that the test tooling expects -->
<add-linker name="xsiframe" />

<!-- define url to load dh-internal from, grpc server to connect to -->
<define-configuration-property name="dh.server" is-multi-valued="false"/>
</module>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<module>
<inherits name="io.deephaven.web.DeephavenApi" />

<!-- restore the default linker that the test tooling expects -->
<add-linker name="xsiframe" />


<!-- define url to load dh-internal from, grpc server to connect to -->
<define-configuration-property name="dh.server" is-multi-valued="false"/>
<!-- set a value so that the app can compile at all -->
<set-configuration-property name="dh.server" value="notset" />
</module>
Loading

0 comments on commit e7af7a1

Please sign in to comment.