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.assertNull; 28 import static org.junit.Assert.assertSame; 29 import static org.junit.Assert.fail; 30 import static org.mockito.Mockito.mock; 31 32 import com.google.common.base.Objects; 33 import io.grpc.ClientStreamTracer.StreamInfo; 34 import io.grpc.internal.SerializingExecutor; 35 import java.util.concurrent.Executor; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 import org.junit.runners.JUnit4; 39 40 /** Unit tests for {@link CallOptions}. */ 41 @RunWith(JUnit4.class) 42 public class CallOptionsTest { 43 private static final CallOptions.Key<String> OPTION_1 44 = CallOptions.Key.createWithDefault("option1", "default"); 45 private static final CallOptions.Key<String> OPTION_2 46 = CallOptions.Key.createWithDefault("option2", "default"); 47 private final String sampleAuthority = "authority"; 48 private final String sampleCompressor = "compressor"; 49 private final Deadline.Ticker ticker = new FakeTicker(); 50 private final Deadline sampleDeadline = Deadline.after(1, NANOSECONDS, ticker); 51 private final CallCredentials sampleCreds = mock(CallCredentials.class); 52 private final ClientStreamTracer.Factory tracerFactory1 = new FakeTracerFactory("tracerFactory1"); 53 private final ClientStreamTracer.Factory tracerFactory2 = new FakeTracerFactory("tracerFactory2"); 54 private final CallOptions allSet = CallOptions.DEFAULT 55 .withAuthority(sampleAuthority) 56 .withDeadline(sampleDeadline) 57 .withCallCredentials(sampleCreds) 58 .withCompression(sampleCompressor) 59 .withWaitForReady() 60 .withExecutor(directExecutor()) 61 .withOption(OPTION_1, "value1") 62 .withStreamTracerFactory(tracerFactory1) 63 .withOption(OPTION_2, "value2") 64 .withStreamTracerFactory(tracerFactory2); 65 66 @Test defaultsAreAllNull()67 public void defaultsAreAllNull() { 68 assertThat(CallOptions.DEFAULT.getDeadline()).isNull(); 69 assertThat(CallOptions.DEFAULT.getAuthority()).isNull(); 70 assertThat(CallOptions.DEFAULT.getExecutor()).isNull(); 71 assertThat(CallOptions.DEFAULT.getCredentials()).isNull(); 72 assertThat(CallOptions.DEFAULT.getCompressor()).isNull(); 73 assertThat(CallOptions.DEFAULT.isWaitForReady()).isFalse(); 74 assertThat(CallOptions.DEFAULT.getStreamTracerFactories()).isEmpty(); 75 } 76 77 @Test withAndWithoutWaitForReady()78 public void withAndWithoutWaitForReady() { 79 assertThat(CallOptions.DEFAULT.withWaitForReady().isWaitForReady()).isTrue(); 80 assertThat(CallOptions.DEFAULT.withWaitForReady().withoutWaitForReady().isWaitForReady()) 81 .isFalse(); 82 } 83 84 @Test allWiths()85 public void allWiths() { 86 assertThat(allSet.getAuthority()).isSameInstanceAs(sampleAuthority); 87 assertThat(allSet.getDeadline()).isSameInstanceAs(sampleDeadline); 88 assertThat(allSet.getCredentials()).isSameInstanceAs(sampleCreds); 89 assertThat(allSet.getCompressor()).isSameInstanceAs(sampleCompressor); 90 assertThat(allSet.getExecutor()).isSameInstanceAs(directExecutor()); 91 assertThat(allSet.getOption(OPTION_1)).isSameInstanceAs("value1"); 92 assertThat(allSet.getOption(OPTION_2)).isSameInstanceAs("value2"); 93 assertThat(allSet.isWaitForReady()).isTrue(); 94 } 95 96 @Test noStrayModifications()97 public void noStrayModifications() { 98 assertThat(equal(allSet, allSet.withAuthority("blah").withAuthority(sampleAuthority))) 99 .isTrue(); 100 assertThat( 101 equal(allSet, 102 allSet.withDeadline(Deadline.after(314, NANOSECONDS)).withDeadline(sampleDeadline))) 103 .isTrue(); 104 assertThat( 105 equal(allSet, 106 allSet.withCallCredentials(mock(CallCredentials.class)) 107 .withCallCredentials(sampleCreds))) 108 .isTrue(); 109 } 110 111 @Test mutation()112 public void mutation() { 113 Deadline deadline = Deadline.after(10, SECONDS); 114 CallOptions options1 = CallOptions.DEFAULT.withDeadline(deadline); 115 assertThat(CallOptions.DEFAULT.getDeadline()).isNull(); 116 assertThat(deadline).isSameInstanceAs(options1.getDeadline()); 117 118 CallOptions options2 = options1.withDeadline(null); 119 assertThat(deadline).isSameInstanceAs(options1.getDeadline()); 120 assertThat(options2.getDeadline()).isNull(); 121 } 122 123 @Test mutateExecutor()124 public void mutateExecutor() { 125 Executor executor = directExecutor(); 126 CallOptions options1 = CallOptions.DEFAULT.withExecutor(executor); 127 assertThat(CallOptions.DEFAULT.getExecutor()).isNull(); 128 assertThat(executor).isSameInstanceAs(options1.getExecutor()); 129 130 CallOptions options2 = options1.withExecutor(null); 131 assertThat(executor).isSameInstanceAs(options1.getExecutor()); 132 assertThat(options2.getExecutor()).isNull(); 133 } 134 135 @Test withDeadlineAfter()136 public void withDeadlineAfter() { 137 Deadline actual = CallOptions.DEFAULT.withDeadlineAfter(1, MINUTES).getDeadline(); 138 Deadline expected = Deadline.after(1, MINUTES); 139 140 assertAbout(deadline()).that(actual).isWithin(10, MILLISECONDS).of(expected); 141 } 142 143 @Test toStringMatches_noDeadline_default()144 public void toStringMatches_noDeadline_default() { 145 String actual = allSet 146 .withDeadline(null) 147 .withExecutor(new SerializingExecutor(directExecutor())) 148 .withCallCredentials(null) 149 .withMaxInboundMessageSize(44) 150 .withMaxOutboundMessageSize(55) 151 .toString(); 152 153 assertThat(actual).contains("deadline=null"); 154 assertThat(actual).contains("authority=authority"); 155 assertThat(actual).contains("callCredentials=null"); 156 assertThat(actual).contains("executor=class io.grpc.internal.SerializingExecutor"); 157 assertThat(actual).contains("compressorName=compressor"); 158 assertThat(actual).contains("customOptions=[[option1, value1], [option2, value2]]"); 159 assertThat(actual).contains("waitForReady=true"); 160 assertThat(actual).contains("maxInboundMessageSize=44"); 161 assertThat(actual).contains("maxOutboundMessageSize=55"); 162 assertThat(actual).contains("streamTracerFactories=[tracerFactory1, tracerFactory2]"); 163 } 164 165 @Test toStringMatches_noDeadline()166 public void toStringMatches_noDeadline() { 167 String actual = CallOptions.DEFAULT.toString(); 168 assertThat(actual).contains("deadline=null"); 169 } 170 171 @Test toStringMatches_withDeadline()172 public void toStringMatches_withDeadline() { 173 assertThat(allSet.toString()).contains("0.000000001s from now"); 174 } 175 176 @Test withCustomOptionDefault()177 public void withCustomOptionDefault() { 178 CallOptions opts = CallOptions.DEFAULT; 179 assertThat(opts.getOption(OPTION_1)).isEqualTo("default"); 180 } 181 182 @Test withCustomOption()183 public void withCustomOption() { 184 CallOptions opts = CallOptions.DEFAULT.withOption(OPTION_1, "v1"); 185 assertThat(opts.getOption(OPTION_1)).isEqualTo("v1"); 186 } 187 188 @Test withCustomOptionLastOneWins()189 public void withCustomOptionLastOneWins() { 190 CallOptions opts = CallOptions.DEFAULT.withOption(OPTION_1, "v1").withOption(OPTION_1, "v2"); 191 assertThat(opts.getOption(OPTION_1)).isEqualTo("v2"); 192 } 193 194 @Test withMultipleCustomOption()195 public void withMultipleCustomOption() { 196 CallOptions opts = CallOptions.DEFAULT.withOption(OPTION_1, "v1").withOption(OPTION_2, "v2"); 197 assertThat(opts.getOption(OPTION_1)).isEqualTo("v1"); 198 assertThat(opts.getOption(OPTION_2)).isEqualTo("v2"); 199 } 200 201 @Test withOptionDoesNotMutateOriginal()202 public void withOptionDoesNotMutateOriginal() { 203 CallOptions defaultOpt = CallOptions.DEFAULT; 204 CallOptions opt1 = defaultOpt.withOption(OPTION_1, "v1"); 205 CallOptions opt2 = opt1.withOption(OPTION_1, "v2"); 206 207 assertThat(defaultOpt.getOption(OPTION_1)).isEqualTo("default"); 208 assertThat(opt1.getOption(OPTION_1)).isEqualTo("v1"); 209 assertThat(opt2.getOption(OPTION_1)).isEqualTo("v2"); 210 } 211 212 @Test withStreamTracerFactory()213 public void withStreamTracerFactory() { 214 CallOptions opts1 = CallOptions.DEFAULT.withStreamTracerFactory(tracerFactory1); 215 CallOptions opts2 = opts1.withStreamTracerFactory(tracerFactory2); 216 CallOptions opts3 = opts2.withStreamTracerFactory(tracerFactory2); 217 218 assertThat(opts1.getStreamTracerFactories()).containsExactly(tracerFactory1); 219 assertThat(opts2.getStreamTracerFactories()).containsExactly(tracerFactory1, tracerFactory2) 220 .inOrder(); 221 assertThat(opts3.getStreamTracerFactories()) 222 .containsExactly(tracerFactory1, tracerFactory2, tracerFactory2).inOrder(); 223 224 try { 225 CallOptions.DEFAULT.getStreamTracerFactories().add(tracerFactory1); 226 fail("Should have thrown. The list should be unmodifiable."); 227 } catch (UnsupportedOperationException e) { 228 // Expected 229 } 230 231 try { 232 opts2.getStreamTracerFactories().clear(); 233 fail("Should have thrown. The list should be unmodifiable."); 234 } catch (UnsupportedOperationException e) { 235 // Expected 236 } 237 } 238 239 @Test getWaitForReady()240 public void getWaitForReady() { 241 assertNull(CallOptions.DEFAULT.getWaitForReady()); 242 assertSame(CallOptions.DEFAULT.withWaitForReady().getWaitForReady(), Boolean.TRUE); 243 assertSame(CallOptions.DEFAULT.withoutWaitForReady().getWaitForReady(), Boolean.FALSE); 244 } 245 246 // Only used in noStrayModifications() 247 // TODO(carl-mastrangelo): consider making a CallOptionsSubject for Truth. equal(CallOptions o1, CallOptions o2)248 private static boolean equal(CallOptions o1, CallOptions o2) { 249 return Objects.equal(o1.getDeadline(), o2.getDeadline()) 250 && Objects.equal(o1.getAuthority(), o2.getAuthority()) 251 && Objects.equal(o1.getCredentials(), o2.getCredentials()); 252 } 253 254 private static class FakeTicker extends Deadline.Ticker { 255 private long time; 256 257 @Override nanoTime()258 public long nanoTime() { 259 return time; 260 } 261 } 262 263 private static class FakeTracerFactory extends ClientStreamTracer.Factory { 264 final String name; 265 FakeTracerFactory(String name)266 FakeTracerFactory(String name) { 267 this.name = name; 268 } 269 270 @Override newClientStreamTracer(StreamInfo info, Metadata headers)271 public ClientStreamTracer newClientStreamTracer(StreamInfo info, Metadata headers) { 272 return new ClientStreamTracer() {}; 273 } 274 275 @Override toString()276 public String toString() { 277 return name; 278 } 279 } 280 } 281