/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.beam.fn.harness.test; import io.grpc.stub.CallStreamObserver; import io.grpc.stub.StreamObserver; import java.util.function.Consumer; import java.util.function.Supplier; /** Utility methods which enable testing of {@link StreamObserver}s. */ public class TestStreams { /** * Creates a test {@link CallStreamObserver} {@link Builder} that forwards * {@link StreamObserver#onNext} calls to the supplied {@link Consumer}. */ public static <T> Builder<T> withOnNext(Consumer<T> onNext) { return new Builder<>(new ForwardingCallStreamObserver<>( onNext, TestStreams::noop, TestStreams::noop, TestStreams::returnTrue)); } /** A builder for a test {@link CallStreamObserver} that performs various callbacks. */ public static class Builder<T> { private final ForwardingCallStreamObserver<T> observer; private Builder(ForwardingCallStreamObserver<T> observer) { this.observer = observer; } /** * Returns a new {@link Builder} like this one with the specified * {@link CallStreamObserver#isReady} callback. */ public Builder<T> withIsReady(Supplier<Boolean> isReady) { return new Builder<>(new ForwardingCallStreamObserver<>( observer.onNext, observer.onError, observer.onCompleted, isReady)); } /** * Returns a new {@link Builder} like this one with the specified * {@link StreamObserver#onCompleted} callback. */ public Builder<T> withOnCompleted(Runnable onCompleted) { return new Builder<>(new ForwardingCallStreamObserver<>( observer.onNext, observer.onError, onCompleted, observer.isReady)); } /** * Returns a new {@link Builder} like this one with the specified * {@link StreamObserver#onError} callback. */ public Builder<T> withOnError(Runnable onError) { return new Builder<>(new ForwardingCallStreamObserver<>( observer.onNext, new Consumer<Throwable>() { @Override public void accept(Throwable t) { onError.run(); } }, observer.onCompleted, observer.isReady)); } /** * Returns a new {@link Builder} like this one with the specified * {@link StreamObserver#onError} consumer. */ public Builder<T> withOnError(Consumer<Throwable> onError) { return new Builder<>(new ForwardingCallStreamObserver<>( observer.onNext, onError, observer.onCompleted, observer.isReady)); } public CallStreamObserver<T> build() { return observer; } } private static void noop() { } private static void noop(Throwable t) { } private static boolean returnTrue() { return true; } /** A {@link CallStreamObserver} which executes the supplied callbacks. */ private static class ForwardingCallStreamObserver<T> extends CallStreamObserver<T> { private final Consumer<T> onNext; private final Supplier<Boolean> isReady; private final Consumer<Throwable> onError; private final Runnable onCompleted; public ForwardingCallStreamObserver( Consumer<T> onNext, Consumer<Throwable> onError, Runnable onCompleted, Supplier<Boolean> isReady) { this.onNext = onNext; this.onError = onError; this.onCompleted = onCompleted; this.isReady = isReady; } @Override public void onNext(T value) { onNext.accept(value); } @Override public void onError(Throwable t) { onError.accept(t); } @Override public void onCompleted() { onCompleted.run(); } @Override public boolean isReady() { return isReady.get(); } @Override public void setOnReadyHandler(Runnable onReadyHandler) {} @Override public void disableAutoInboundFlowControl() {} @Override public void request(int count) {} @Override public void setMessageCompression(boolean enable) {} } }