/* * Copyright 2014 GoDataDriven B.V. * * 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 io.divolte.server; import com.google.common.base.Preconditions; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigValueFactory; import io.divolte.server.config.ValidatedConfiguration; import org.apache.avro.generic.GenericRecord; import javax.annotation.ParametersAreNonnullByDefault; import java.io.IOException; import java.net.ServerSocket; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; public final class ServerTestUtils { /* * Theoretically, this is prone to race conditions, * but in practice, it should be fine due to the way * TCP stacks allocate port numbers (i.e. increment * for the next one). */ public static int findFreePort() { try (final ServerSocket socket = new ServerSocket(0)) { return socket.getLocalPort(); } catch (final IOException e) { return -1; } } @ParametersAreNonnullByDefault public static final class EventPayload { final DivolteEvent event; final AvroRecordBuffer buffer; final GenericRecord record; public EventPayload(final DivolteEvent event, final AvroRecordBuffer buffer, final GenericRecord record) { this.event = Objects.requireNonNull(event); this.buffer = Objects.requireNonNull(buffer); this.record = Objects.requireNonNull(record); } } @ParametersAreNonnullByDefault public static final class TestServer { final Config config; final int port; final Server server; final BlockingQueue<EventPayload> events; public TestServer() { this(findFreePort(), ConfigFactory.parseResources("reference-test.conf")); } public TestServer(final String configResource) { this(findFreePort(), ConfigFactory.parseResources(configResource) .withFallback(ConfigFactory.parseResources("reference-test.conf"))); } public TestServer(final String configResource, final Map<String,Object> extraConfig) { this(findFreePort(), ConfigFactory.parseMap(extraConfig, "Test-specific overrides") .withFallback(ConfigFactory.parseResources(configResource)) .withFallback(ConfigFactory.parseResources("reference-test.conf"))); } private TestServer(final int port, final Config config) { this.port = port; this.config = config.withValue("divolte.global.server.port", ConfigValueFactory.fromAnyRef(port)); events = new ArrayBlockingQueue<>(100); final ValidatedConfiguration vc = new ValidatedConfiguration(() -> this.config); Preconditions.checkArgument(vc.isValid(), "Invalid test server configuration: %s", vc.errors()); server = new Server(vc, (event, buffer, record) -> events.add(new EventPayload(event, buffer, record))); server.run(); } static TestServer createTestServerWithDefaultNonTestConfiguration() { return new TestServer(findFreePort(), ConfigFactory.defaultReference()); } public EventPayload waitForEvent() throws InterruptedException { // SauceLabs can take quite a while to fire up everything. return Optional.ofNullable(events.poll(5, TimeUnit.SECONDS)).orElseThrow(() -> new RuntimeException("Timed out while waiting for server side event to occur.")); } public boolean eventsRemaining() { return !events.isEmpty(); } } }