1/* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19package grpc 20 21import ( 22 "fmt" 23 "math" 24 "reflect" 25 "testing" 26 "time" 27) 28 29func (s) TestParseLoadBalancer(t *testing.T) { 30 testcases := []struct { 31 scjs string 32 wantSC *ServiceConfig 33 wantErr bool 34 }{ 35 { 36 `{ 37 "loadBalancingPolicy": "round_robin", 38 "methodConfig": [ 39 { 40 "name": [ 41 { 42 "service": "foo", 43 "method": "Bar" 44 } 45 ], 46 "waitForReady": true 47 } 48 ] 49}`, 50 &ServiceConfig{ 51 LB: newString("round_robin"), 52 Methods: map[string]MethodConfig{ 53 "/foo/Bar": { 54 WaitForReady: newBool(true), 55 }, 56 }, 57 }, 58 false, 59 }, 60 { 61 `{ 62 "loadBalancingPolicy": 1, 63 "methodConfig": [ 64 { 65 "name": [ 66 { 67 "service": "foo", 68 "method": "Bar" 69 } 70 ], 71 "waitForReady": false 72 } 73 ] 74}`, 75 nil, 76 true, 77 }, 78 } 79 80 for _, c := range testcases { 81 sc, err := parseServiceConfig(c.scjs) 82 if c.wantErr != (err != nil) || !scCompareWithRawJSONSkipped(sc, c.wantSC) { 83 t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, err, c.wantSC, c.wantErr) 84 } 85 } 86} 87 88func (s) TestParseWaitForReady(t *testing.T) { 89 testcases := []struct { 90 scjs string 91 wantSC *ServiceConfig 92 wantErr bool 93 }{ 94 { 95 `{ 96 "methodConfig": [ 97 { 98 "name": [ 99 { 100 "service": "foo", 101 "method": "Bar" 102 } 103 ], 104 "waitForReady": true 105 } 106 ] 107}`, 108 &ServiceConfig{ 109 Methods: map[string]MethodConfig{ 110 "/foo/Bar": { 111 WaitForReady: newBool(true), 112 }, 113 }, 114 }, 115 false, 116 }, 117 { 118 `{ 119 "methodConfig": [ 120 { 121 "name": [ 122 { 123 "service": "foo", 124 "method": "Bar" 125 } 126 ], 127 "waitForReady": false 128 } 129 ] 130}`, 131 &ServiceConfig{ 132 Methods: map[string]MethodConfig{ 133 "/foo/Bar": { 134 WaitForReady: newBool(false), 135 }, 136 }, 137 }, 138 false, 139 }, 140 { 141 `{ 142 "methodConfig": [ 143 { 144 "name": [ 145 { 146 "service": "foo", 147 "method": "Bar" 148 } 149 ], 150 "waitForReady": fall 151 }, 152 { 153 "name": [ 154 { 155 "service": "foo", 156 "method": "Bar" 157 } 158 ], 159 "waitForReady": true 160 } 161 ] 162}`, 163 nil, 164 true, 165 }, 166 } 167 168 for _, c := range testcases { 169 sc, err := parseServiceConfig(c.scjs) 170 if c.wantErr != (err != nil) || !scCompareWithRawJSONSkipped(sc, c.wantSC) { 171 t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, err, c.wantSC, c.wantErr) 172 } 173 } 174} 175 176func (s) TestPraseTimeOut(t *testing.T) { 177 testcases := []struct { 178 scjs string 179 wantSC *ServiceConfig 180 wantErr bool 181 }{ 182 { 183 `{ 184 "methodConfig": [ 185 { 186 "name": [ 187 { 188 "service": "foo", 189 "method": "Bar" 190 } 191 ], 192 "timeout": "1s" 193 } 194 ] 195}`, 196 &ServiceConfig{ 197 Methods: map[string]MethodConfig{ 198 "/foo/Bar": { 199 Timeout: newDuration(time.Second), 200 }, 201 }, 202 }, 203 false, 204 }, 205 { 206 `{ 207 "methodConfig": [ 208 { 209 "name": [ 210 { 211 "service": "foo", 212 "method": "Bar" 213 } 214 ], 215 "timeout": "3c" 216 } 217 ] 218}`, 219 nil, 220 true, 221 }, 222 { 223 `{ 224 "methodConfig": [ 225 { 226 "name": [ 227 { 228 "service": "foo", 229 "method": "Bar" 230 } 231 ], 232 "timeout": "3c" 233 }, 234 { 235 "name": [ 236 { 237 "service": "foo", 238 "method": "Bar" 239 } 240 ], 241 "timeout": "1s" 242 } 243 ] 244}`, 245 nil, 246 true, 247 }, 248 } 249 250 for _, c := range testcases { 251 sc, err := parseServiceConfig(c.scjs) 252 if c.wantErr != (err != nil) || !scCompareWithRawJSONSkipped(sc, c.wantSC) { 253 t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, err, c.wantSC, c.wantErr) 254 } 255 } 256} 257 258func (s) TestPraseMsgSize(t *testing.T) { 259 testcases := []struct { 260 scjs string 261 wantSC *ServiceConfig 262 wantErr bool 263 }{ 264 { 265 `{ 266 "methodConfig": [ 267 { 268 "name": [ 269 { 270 "service": "foo", 271 "method": "Bar" 272 } 273 ], 274 "maxRequestMessageBytes": 1024, 275 "maxResponseMessageBytes": 2048 276 } 277 ] 278}`, 279 &ServiceConfig{ 280 Methods: map[string]MethodConfig{ 281 "/foo/Bar": { 282 MaxReqSize: newInt(1024), 283 MaxRespSize: newInt(2048), 284 }, 285 }, 286 }, 287 false, 288 }, 289 { 290 `{ 291 "methodConfig": [ 292 { 293 "name": [ 294 { 295 "service": "foo", 296 "method": "Bar" 297 } 298 ], 299 "maxRequestMessageBytes": "1024", 300 "maxResponseMessageBytes": "2048" 301 }, 302 { 303 "name": [ 304 { 305 "service": "foo", 306 "method": "Bar" 307 } 308 ], 309 "maxRequestMessageBytes": 1024, 310 "maxResponseMessageBytes": 2048 311 } 312 ] 313}`, 314 nil, 315 true, 316 }, 317 } 318 319 for _, c := range testcases { 320 sc, err := parseServiceConfig(c.scjs) 321 if c.wantErr != (err != nil) || !scCompareWithRawJSONSkipped(sc, c.wantSC) { 322 t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, err, c.wantSC, c.wantErr) 323 } 324 } 325} 326 327func (s) TestParseDuration(t *testing.T) { 328 testCases := []struct { 329 s *string 330 want *time.Duration 331 err bool 332 }{ 333 {s: nil, want: nil}, 334 {s: newString("1s"), want: newDuration(time.Second)}, 335 {s: newString("-1s"), want: newDuration(-time.Second)}, 336 {s: newString("1.1s"), want: newDuration(1100 * time.Millisecond)}, 337 {s: newString("1.s"), want: newDuration(time.Second)}, 338 {s: newString("1.0s"), want: newDuration(time.Second)}, 339 {s: newString(".002s"), want: newDuration(2 * time.Millisecond)}, 340 {s: newString(".002000s"), want: newDuration(2 * time.Millisecond)}, 341 {s: newString("0.003s"), want: newDuration(3 * time.Millisecond)}, 342 {s: newString("0.000004s"), want: newDuration(4 * time.Microsecond)}, 343 {s: newString("5000.000000009s"), want: newDuration(5000*time.Second + 9*time.Nanosecond)}, 344 {s: newString("4999.999999999s"), want: newDuration(5000*time.Second - time.Nanosecond)}, 345 {s: newString("1"), err: true}, 346 {s: newString("s"), err: true}, 347 {s: newString(".s"), err: true}, 348 {s: newString("1 s"), err: true}, 349 {s: newString(" 1s"), err: true}, 350 {s: newString("1ms"), err: true}, 351 {s: newString("1.1.1s"), err: true}, 352 {s: newString("Xs"), err: true}, 353 {s: newString("as"), err: true}, 354 {s: newString(".0000000001s"), err: true}, 355 {s: newString(fmt.Sprint(math.MaxInt32) + "s"), want: newDuration(math.MaxInt32 * time.Second)}, 356 {s: newString(fmt.Sprint(int64(math.MaxInt32)+1) + "s"), err: true}, 357 } 358 for _, tc := range testCases { 359 got, err := parseDuration(tc.s) 360 if tc.err != (err != nil) || 361 (got == nil) != (tc.want == nil) || 362 (got != nil && *got != *tc.want) { 363 wantErr := "<nil>" 364 if tc.err { 365 wantErr = "<non-nil error>" 366 } 367 s := "<nil>" 368 if tc.s != nil { 369 s = `&"` + *tc.s + `"` 370 } 371 t.Errorf("parseDuration(%v) = %v, %v; want %v, %v", s, got, err, tc.want, wantErr) 372 } 373 } 374} 375 376func newBool(b bool) *bool { 377 return &b 378} 379 380func newDuration(b time.Duration) *time.Duration { 381 return &b 382} 383 384func newString(b string) *string { 385 return &b 386} 387 388func scCompareWithRawJSONSkipped(s1, s2 *ServiceConfig) bool { 389 if s1 == nil && s2 == nil { 390 return true 391 } 392 if (s1 == nil) != (s2 == nil) { 393 return false 394 } 395 s1.rawJSONString = "" 396 s2.rawJSONString = "" 397 return reflect.DeepEqual(s1, s2) 398} 399