1 /* 2 * Copyright 2015 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc; 18 19 import static com.google.common.truth.Truth.assertAbout; 20 import static com.google.common.truth.Truth.assertThat; 21 import static com.google.common.util.concurrent.MoreExecutors.directExecutor; 22 import static io.grpc.testing.DeadlineSubject.deadline; 23 import static java.util.concurrent.TimeUnit.MILLISECONDS; 24 import static java.util.concurrent.TimeUnit.MINUTES; 25 import static java.util.concurrent.TimeUnit.NANOSECONDS; 26 import static java.util.concurrent.TimeUnit.SECONDS; 27 import static org.junit.Assert.fail; 28 import static org.mockito.Mockito.mock; 29 30 import com.google.common.base.Objects; 31 import io.grpc.internal.SerializingExecutor; 32 import java.util.concurrent.Executor; 33 import java.util.concurrent.TimeUnit; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 import org.junit.runners.JUnit4; 37 38 /** Unit tests for {@link CallOptions}. */ 39 @RunWith(JUnit4.class) 40 public class CallOptionsTest { 41 private static final CallOptions.Key<String> OPTION_1 42 = CallOptions.Key.createWithDefault("option1", "default"); 43 private static final CallOptions.Key<String> OPTION_2 44 = CallOptions.Key.createWithDefault("option2", "default"); 45 private final String sampleAuthority = "authority"; 46 private final String sampleCompressor = "compressor"; 47 private final Deadline.Ticker ticker = new FakeTicker(); 48 private final Deadline sampleDeadline = Deadline.after(1, NANOSECONDS, ticker); 49 private final CallCredentials sampleCreds = mock(CallCredentials.class); 50 private final ClientStreamTracer.Factory tracerFactory1 = new FakeTracerFactory("tracerFactory1"); 51 private final ClientStreamTracer.Factory tracerFactory2 = new FakeTracerFactory("tracerFactory2"); 52 private final CallOptions allSet = CallOptions.DEFAULT 53 .withAuthority(sampleAuthority) 54 .withDeadline(sampleDeadline) 55 .withCallCredentials(sampleCreds) 56 .withCompression(sampleCompressor) 57 .withWaitForReady() 58 .withExecutor(directExecutor()) 59 .withOption(OPTION_1, "value1") 60 .withStreamTracerFactory(tracerFactory1) 61 .withOption(OPTION_2, "value2") 62 .withStreamTracerFactory(tracerFactory2); 63 64 @Test defaultsAreAllNull()65 public void defaultsAreAllNull() { 66 assertThat(CallOptions.DEFAULT.getDeadline()).isNull(); 67 assertThat(CallOptions.DEFAULT.getAuthority()).isNull(); 68 assertThat(CallOptions.DEFAULT.getExecutor()).isNull(); 69 assertThat(CallOptions.DEFAULT.getCredentials()).isNull(); 70 assertThat(CallOptions.DEFAULT.getCompressor()).isNull(); 71 assertThat(CallOptions.DEFAULT.isWaitForReady()).isFalse(); 72 assertThat(CallOptions.DEFAULT.getStreamTracerFactories()).isEmpty(); 73 } 74 75 @Test withAndWithoutWaitForReady()76 public void withAndWithoutWaitForReady() { 77 assertThat(CallOptions.DEFAULT.withWaitForReady().isWaitForReady()).isTrue(); 78 assertThat(CallOptions.DEFAULT.withWaitForReady().withoutWaitForReady().isWaitForReady()) 79 .isFalse(); 80 } 81 82 @Test allWiths()83 public void allWiths() { 84 assertThat(allSet.getAuthority()).isSameAs(sampleAuthority); 85 assertThat(allSet.getDeadline()).isSameAs(sampleDeadline); 86 assertThat(allSet.getCredentials()).isSameAs(sampleCreds); 87 assertThat(allSet.getCompressor()).isSameAs(sampleCompressor); 88 assertThat(allSet.getExecutor()).isSameAs(directExecutor()); 89 assertThat(allSet.getOption(OPTION_1)).isSameAs("value1"); 90 assertThat(allSet.getOption(OPTION_2)).isSameAs("value2"); 91 assertThat(allSet.isWaitForReady()).isTrue(); 92 } 93 94 @Test noStrayModifications()95 public void noStrayModifications() { 96 assertThat(equal(allSet, allSet.withAuthority("blah").withAuthority(sampleAuthority))) 97 .isTrue(); 98 assertThat( 99 equal(allSet, 100 allSet.withDeadline(Deadline.after(314, NANOSECONDS)).withDeadline(sampleDeadline))) 101 .isTrue(); 102 assertThat( 103 equal(allSet, 104 allSet.withCallCredentials(mock(CallCredentials.class)) 105 .withCallCredentials(sampleCreds))) 106 .isTrue(); 107 } 108 109 @Test mutation()110 public void mutation() { 111 Deadline deadline = Deadline.after(10, SECONDS); 112 CallOptions options1 = CallOptions.DEFAULT.withDeadline(deadline); 113 assertThat(CallOptions.DEFAULT.getDeadline()).isNull(); 114 assertThat(deadline).isSameAs(options1.getDeadline()); 115 116 CallOptions options2 = options1.withDeadline(null); 117 assertThat(deadline).isSameAs(options1.getDeadline()); 118 assertThat(options2.getDeadline()).isNull(); 119 } 120 121 @Test mutateExecutor()122 public void mutateExecutor() { 123 Executor executor = directExecutor(); 124 CallOptions options1 = CallOptions.DEFAULT.withExecutor(executor); 125 assertThat(CallOptions.DEFAULT.getExecutor()).isNull(); 126 assertThat(executor).isSameAs(options1.getExecutor()); 127 128 CallOptions options2 = options1.withExecutor(null); 129 assertThat(executor).isSameAs(options1.getExecutor()); 130 assertThat(options2.getExecutor()).isNull(); 131 } 132 133 @Test withDeadlineAfter()134 public void withDeadlineAfter() { 135 Deadline actual = CallOptions.DEFAULT.withDeadlineAfter(1, MINUTES).getDeadline(); 136 Deadline expected = Deadline.after(1, MINUTES); 137 138 assertAbout(deadline()).that(actual).isWithin(10, MILLISECONDS).of(expected); 139 } 140 141 @Test toStringMatches_noDeadline_default()142 public void toStringMatches_noDeadline_default() { 143 String actual = allSet 144 .withDeadline(null) 145 .withExecutor(new SerializingExecutor(directExecutor())) 146 .withCallCredentials(null) 147 .withMaxInboundMessageSize(44) 148 .withMaxOutboundMessageSize(55) 149 .toString(); 150 151 assertThat(actual).contains("deadline=null"); 152 assertThat(actual).contains("authority=authority"); 153 assertThat(actual).contains("callCredentials=null"); 154 assertThat(actual).contains("executor=class io.grpc.internal.SerializingExecutor"); 155 assertThat(actual).contains("compressorName=compressor"); 156 assertThat(actual).contains("customOptions=[[option1, value1], [option2, value2]]"); 157 assertThat(actual).contains("waitForReady=true"); 158 assertThat(actual).contains("maxInboundMessageSize=44"); 159 assertThat(actual).contains("maxOutboundMessageSize=55"); 160 assertThat(actual).contains("streamTracerFactories=[tracerFactory1, tracerFactory2]"); 161 } 162 163 @Test toStringMatches_noDeadline()164 public void toStringMatches_noDeadline() { 165 String actual = CallOptions.DEFAULT.toString(); 166 assertThat(actual).contains("deadline=null"); 167 } 168 169 @Test toStringMatches_withDeadline()170 public void toStringMatches_withDeadline() { 171 assertThat(allSet.toString()).contains("1 ns from now"); 172 } 173 174 @Test withCustomOptionDefault()175 public void withCustomOptionDefault() { 176 CallOptions opts = CallOptions.DEFAULT; 177 assertThat(opts.getOption(OPTION_1)).isEqualTo("default"); 178 } 179 180 @Test withCustomOption()181 public void withCustomOption() { 182 CallOptions opts = CallOptions.DEFAULT.withOption(OPTION_1, "v1"); 183 assertThat(opts.getOption(OPTION_1)).isEqualTo("v1"); 184 } 185 186 @Test withCustomOptionLastOneWins()187 public void withCustomOptionLastOneWins() { 188 CallOptions opts = CallOptions.DEFAULT.withOption(OPTION_1, "v1").withOption(OPTION_1, "v2"); 189 assertThat(opts.getOption(OPTION_1)).isEqualTo("v2"); 190 } 191 192 @Test withMultipleCustomOption()193 public void withMultipleCustomOption() { 194 CallOptions opts = CallOptions.DEFAULT.withOption(OPTION_1, "v1").withOption(OPTION_2, "v2"); 195 assertThat(opts.getOption(OPTION_1)).isEqualTo("v1"); 196 assertThat(opts.getOption(OPTION_2)).isEqualTo("v2"); 197 } 198 199 @Test withStreamTracerFactory()200 public void withStreamTracerFactory() { 201 CallOptions opts1 = CallOptions.DEFAULT.withStreamTracerFactory(tracerFactory1); 202 CallOptions opts2 = opts1.withStreamTracerFactory(tracerFactory2); 203 CallOptions opts3 = opts2.withStreamTracerFactory(tracerFactory2); 204 205 assertThat(opts1.getStreamTracerFactories()).containsExactly(tracerFactory1); 206 assertThat(opts2.getStreamTracerFactories()).containsExactly(tracerFactory1, tracerFactory2) 207 .inOrder(); 208 assertThat(opts3.getStreamTracerFactories()) 209 .containsExactly(tracerFactory1, tracerFactory2, tracerFactory2).inOrder(); 210 211 try { 212 CallOptions.DEFAULT.getStreamTracerFactories().add(tracerFactory1); 213 fail("Should have thrown. The list should be unmodifiable."); 214 } catch (UnsupportedOperationException e) { 215 // Expected 216 } 217 218 try { 219 opts2.getStreamTracerFactories().clear(); 220 fail("Should have thrown. The list should be unmodifiable."); 221 } catch (UnsupportedOperationException e) { 222 // Expected 223 } 224 } 225 226 // Only used in noStrayModifications() 227 // TODO(carl-mastrangelo): consider making a CallOptionsSubject for Truth. equal(CallOptions o1, CallOptions o2)228 private static boolean equal(CallOptions o1, CallOptions o2) { 229 return Objects.equal(o1.getDeadline(), o2.getDeadline()) 230 && Objects.equal(o1.getAuthority(), o2.getAuthority()) 231 && Objects.equal(o1.getCredentials(), o2.getCredentials()); 232 } 233 234 private static class FakeTicker extends Deadline.Ticker { 235 private long time; 236 237 @Override read()238 public long read() { 239 return time; 240 } 241 reset(long time)242 public void reset(long time) { 243 this.time = time; 244 } 245 increment(long period, TimeUnit unit)246 public void increment(long period, TimeUnit unit) { 247 if (period < 0) { 248 throw new IllegalArgumentException(); 249 } 250 this.time += unit.toNanos(period); 251 } 252 } 253 254 private static class FakeTracerFactory extends ClientStreamTracer.Factory { 255 final String name; 256 FakeTracerFactory(String name)257 FakeTracerFactory(String name) { 258 this.name = name; 259 } 260 261 @Override newClientStreamTracer(CallOptions callOptions, Metadata headers)262 public ClientStreamTracer newClientStreamTracer(CallOptions callOptions, Metadata headers) { 263 return new ClientStreamTracer() {}; 264 } 265 266 @Override toString()267 public String toString() { 268 return name; 269 } 270 } 271 } 272