github.com/google/cloudprober@v0.11.3/common/message/message_test.go (about) 1 // Copyright 2017-2018 The Cloudprober Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package message 16 17 import ( 18 "testing" 19 "time" 20 21 "github.com/golang/protobuf/proto" 22 msgpb "github.com/google/cloudprober/common/message/proto" 23 ) 24 25 // createMessage is a helper function for creating a message and fatally failing 26 // if CreateMessage fails. This is for use in places where we don't expect 27 // CreateMessage to fail. 28 func createMessage(t *testing.T, fs *FlowState, ts time.Time) ([]byte, uint64) { 29 return createMessageWithPayload(t, fs, ts, nil) 30 } 31 32 // createMessageWithPayload is a helper function for creating a message and 33 // fatally failing if CreateMessage fails. This is for use in places where we 34 // don't expect CreateMessage to fail. 35 func createMessageWithPayload(t *testing.T, fs *FlowState, ts time.Time, payload []byte) ([]byte, uint64) { 36 maxLen := 1024 37 msgBytes, msgSeq, err := fs.CreateMessage(ts, payload, maxLen) 38 if err != nil { 39 t.Fatalf("Error creating message for seq %d: %v", fs.seq+1, err) 40 } 41 return msgBytes, msgSeq 42 } 43 44 // TestUint64Conversion tests the conversion of uint64 from and to network byte order. 45 func TestUint64Conversion(t *testing.T) { 46 val := uint64(0) 47 for i := uint64(0); i < 10; i++ { 48 inp := val + i 49 bytes := Uint64ToNetworkBytes(val + i) 50 out := NetworkBytesToUint64(bytes) 51 if inp != out { 52 t.Errorf("Conversion pipeline failed: got %d want %d", out, inp) 53 } 54 inp = val - i 55 bytes = Uint64ToNetworkBytes(inp) 56 out = NetworkBytesToUint64(bytes) 57 if inp != out { 58 t.Errorf("Conversion pipeline failed: got %d want %d", out, inp) 59 } 60 } 61 } 62 63 // TestMessageEncodeDecode tests encoding/decoding of properly formed msgs. 64 func TestMessageEncodeDecode(t *testing.T) { 65 txFSM := NewFlowStateMap() 66 rxFSM := NewFlowStateMap() 67 68 src := "aa-src" 69 dst := "zz-dst" 70 seq := uint64(100) 71 ts := time.Now().Truncate(time.Microsecond) 72 73 txFS := txFSM.FlowState(src, "", dst) 74 txFS.seq = seq - 1 75 msgBytes, msgSeq := createMessage(t, txFS, ts) 76 if msgSeq != seq { 77 t.Errorf("Incorrect seq in message: got %d want %d", msgSeq, seq) 78 } 79 80 // Pre-create flow state on the rx side. So we expect success in every step. 81 rxFS := rxFSM.FlowState(src, "", dst) 82 rxFS.seq = seq - 1 83 msg, err := NewMessage(msgBytes) 84 if err != nil { 85 t.Fatalf("Process message failure: %v", err) 86 } 87 if msg.Src() != src || msg.Dst() != dst { 88 t.Errorf("Message content error (src, dst): got (%s, %s) want (%s, %s)", msg.Src(), msg.Dst(), src, dst) 89 } 90 res := msg.ProcessOneWay(rxFSM, ts.Add(time.Second)) 91 if rxFS.seq != seq { 92 t.Errorf("Seq number mismatch. got %d want %d. %v %v", rxFS.seq, seq, rxFS, res) 93 } 94 if !res.Success || res.LostCount > 0 || res.Delayed { 95 t.Errorf("Success, lostCount, delayed mismatch. got (%v %v %v) want (%v %v %v)", 96 res.Success, res.LostCount, res.Delayed, true, 0, false) 97 } 98 } 99 100 // TestInvalidMessages tests encoding/decoding error paths. 101 func TestInvalidMessage(t *testing.T) { 102 fss := NewFlowStateMap() 103 104 src := "aa-src" 105 dst := "zz-dst" 106 seq := uint64(100) 107 ts := time.Now().Truncate(time.Microsecond) 108 maxLen := 10 109 110 fs := fss.FlowState(src, "", dst) 111 if msgBytes, _, err := fs.CreateMessage(ts, nil, maxLen); err == nil { 112 t.Errorf("Message too long(%d) for maxlen(%d) but did not fail.", len(msgBytes), maxLen) 113 } 114 115 // Invalid magic. 116 msg := &msgpb.Msg{ 117 Magic: proto.Uint64(constants.GetMagic() + 1), 118 Seq: Uint64ToNetworkBytes(seq), 119 Src: &msgpb.DataNode{ 120 Name: proto.String(src), 121 }, 122 Dst: &msgpb.DataNode{ 123 Name: proto.String(dst), 124 }, 125 } 126 msgBytes, err := proto.Marshal(msg) 127 if err != nil { 128 t.Fatalf("Error marshalling message: %v", err) 129 } 130 131 if _, err := NewMessage(msgBytes); err == nil { 132 t.Error("ProcessMessage expected to fail due to invalid magic but did not fail") 133 } 134 } 135 136 // TestSeqHandling tests various sequence number cases. 137 func TestSeqHandling(t *testing.T) { 138 txFSM := NewFlowStateMap() 139 rxFSM := NewFlowStateMap() 140 141 src := "aa-src" 142 dst := "zz-dst" 143 seq := uint64(100) 144 pktTS := time.Now().Truncate(time.Microsecond) 145 rxTS := pktTS.Add(time.Millisecond) 146 147 // Create a message and revert it. 148 txFS := txFSM.FlowState(src, "1", dst) 149 txFS.seq = seq - 1 150 msgBytes, msgSeq := createMessage(t, txFS, pktTS) 151 152 if !txFS.WithdrawMessage(msgSeq) || txFS.seq != seq-1 { 153 t.Errorf("WithdrawMessage failed: NextSeq %d msgSeq %d fs.seq %d", seq, msgSeq, txFS.seq) 154 } 155 // withdraw an older message and expect failure. 156 if txFS.WithdrawMessage(seq - 2) { 157 t.Errorf("WithdrawMessage succeeded: msgSeq %d fs.seq %d", seq-2, txFS.seq) 158 } 159 160 txFS.seq = seq - 1 161 msgBytes, msgSeq = createMessage(t, txFS, pktTS) 162 // Receive the message and process it. Seq and srcs should match. 163 // This will be the first message for the flow: 164 // => Flowstate should be created. 165 // => We expect success in the result. 166 msg, err := NewMessage(msgBytes) 167 if err != nil { 168 t.Fatalf("Error processing message: %v", err) 169 } 170 res := msg.ProcessOneWay(rxFSM, rxTS) 171 rxFS := res.FS 172 if rxFS == nil || rxFSM.FlowState(src, "1", dst) != rxFS { 173 t.Errorf("Expected sender to appear in FlowStateMap struct, got %v", rxFSM.FlowState(src, "1", dst)) 174 } 175 if rxFS.src != src { 176 t.Errorf("Message content error - src: got %s want %s", rxFS.src, src) 177 } 178 if rxFS.srcPort != "1" { 179 t.Errorf("Message content error - srcPort: got %s want 1", rxFS.srcPort) 180 } 181 if rxFS.seq != seq { 182 t.Errorf("Seq number mismatch. got %d want %d.", rxFS.seq, seq) 183 } 184 if !res.Success || res.LostCount > 0 || res.Delayed { 185 t.Errorf("Success, lostCount, delayed mismatch. got (%v %v %v) want (%v %v %v)", 186 res.Success, res.LostCount, res.Delayed, true, 0, false) 187 } 188 189 // Send a message with an older seq number. 190 pktTS = pktTS.Add(time.Second) 191 rxTS = rxTS.Add(time.Second) 192 txFS.seq = seq - 10 193 msgBytes, msgSeq = createMessage(t, txFS, pktTS) 194 if res.FS.msgTS == pktTS || res.FS.rxTS == rxTS { 195 t.Errorf("Timestamps not updated. got (%s, %s) want (%s, %s)", res.FS.msgTS, res.FS.rxTS, pktTS, rxTS) 196 } 197 198 // Send a message with seq+1. 199 // pktTS = prevPktTS + 1sec. 200 // rxTS = prevRxTS + 1 sec + 1 millisecond. 201 // => ipd = 1 millisecond 202 ipd := time.Millisecond 203 pktTS = pktTS.Add(time.Second) 204 rxTS = rxTS.Add(time.Second + ipd) 205 txFS.seq = seq 206 msgBytes, msgSeq = createMessage(t, txFS, pktTS) 207 if msg, err = NewMessage(msgBytes); err != nil { 208 t.Fatalf("Error processing message: %v", err) 209 } 210 res = msg.ProcessOneWay(rxFSM, rxTS) 211 rxFS = res.FS 212 if !res.Success { 213 t.Errorf("Got failure, want success. tx seq: %d, rx want seq: %d", txFS.seq, rxFS.seq+1) 214 } 215 if res.InterPktDelay != ipd { 216 t.Errorf("InterPktDelay calculation error got %v want %v", res.InterPktDelay, ipd) 217 } 218 if res.FS.msgTS != pktTS || res.FS.rxTS != rxTS { 219 t.Errorf("Timestamps not updated. got (%s, %s) want (%s, %s)", res.FS.msgTS, res.FS.rxTS, pktTS, rxTS) 220 } 221 222 // Send a message with lost packets = 10. 223 lost := 10 224 txFS.seq += uint64(lost) 225 pktTS = pktTS.Add(time.Second) 226 rxTS = rxTS.Add(time.Second) 227 msgBytes, msgSeq = createMessage(t, txFS, pktTS) 228 if msg, err = NewMessage(msgBytes); err != nil { 229 t.Fatalf("Error processing message: %v", err) 230 } 231 res = msg.ProcessOneWay(rxFSM, rxTS) 232 rxFS = res.FS 233 if res.Success { 234 t.Error("Got success, want failure.") 235 } 236 if res.LostCount != lost { 237 t.Errorf("Got lostcount=%d want %d tx seq: %d, rx want seq: %d", res.LostCount, lost, txFS.seq, rxFS.seq+1) 238 } 239 if res.FS.msgTS != pktTS || res.FS.rxTS != rxTS { 240 t.Errorf("Timestamps not updated. got (%s, %s) want (%s, %s)", res.FS.msgTS, res.FS.rxTS, pktTS, rxTS) 241 } 242 } 243 244 // TestMultiplePorts seq handling for multiple src ports. 245 func TestMultiplePorts(t *testing.T) { 246 txFSM := NewFlowStateMap() 247 rxFSM := NewFlowStateMap() 248 249 src := "aa-src" 250 dst := "zz-dst" 251 seq := uint64(100) 252 pktTS := time.Now().Truncate(time.Microsecond) 253 rxTS := pktTS.Add(time.Millisecond) 254 255 // Receive the message and process it. Seq and srcs should match. 256 // This will be the first message for the flow => Flowstate should be created. 257 txFS1 := txFSM.FlowState(src, "1", dst) 258 txFS1.seq = seq - 1 259 msgBytes, _ := createMessage(t, txFS1, pktTS) 260 msg, err := NewMessage(msgBytes) 261 if err != nil { 262 t.Fatalf("Error processing message: %v", err) 263 } 264 res := msg.ProcessOneWay(rxFSM, rxTS) 265 266 // Send a message with seq+1. 267 // pktTS = prevPktTS + 1sec. 268 // rxTS = prevRxTS + 1 sec + 1 millisecond. 269 // => ipd = 1 millisecond 270 ipd := time.Millisecond 271 pktTS = pktTS.Add(time.Second) 272 rxTS = rxTS.Add(time.Second + ipd) 273 txFS1.seq = seq 274 msgBytes, _ = createMessage(t, txFS1, pktTS) 275 msg, err = NewMessage(msgBytes) 276 if err != nil { 277 t.Fatalf("Error processing message: %v", err) 278 } 279 res = msg.ProcessOneWay(rxFSM, rxTS) 280 rxFS1 := res.FS 281 if rxFS1 == nil || rxFSM.FlowState(src, "1", dst) != rxFS1 { 282 t.Errorf("Expected sender to appear in FlowStateMap struct, got %v", rxFSM.FlowState(src, "1", dst)) 283 } 284 if !res.Success { 285 t.Errorf("Got failure, want success. tx seq: %d, rx want seq: %d", txFS1.seq, rxFS1.seq+1) 286 } 287 if res.InterPktDelay != ipd { 288 t.Errorf("InterPktDelay calculation error got %v want %v", res.InterPktDelay, ipd) 289 } 290 if res.FS.msgTS != pktTS || res.FS.rxTS != rxTS { 291 t.Errorf("Timestamps not updated. got (%s, %s) want (%s, %s)", res.FS.msgTS, res.FS.rxTS, pktTS, rxTS) 292 } 293 294 // Send a message with seq+2 on another port. 295 // should not be counted towards port=1 stats. 296 pktTS = pktTS.Add(time.Second) 297 rxTS = rxTS.Add(time.Second + ipd) 298 txFS2 := txFSM.FlowState(src, "2", dst) 299 txFS2.seq = seq + 1 300 msgBytes, _ = createMessage(t, txFS2, pktTS) 301 msg, err = NewMessage(msgBytes) 302 if err != nil { 303 t.Fatalf("Error processing message: %v", err) 304 } 305 // Inject flow state on rx side with seq == msg.Seq - 2 (=> one lost pkt). 306 rxFS2 := rxFSM.FlowState(src, "2", dst) 307 rxFS2.seq = msg.Seq() - 2 308 res = msg.ProcessOneWay(rxFSM, rxTS) 309 if rxFS2.seq != msg.Seq() { 310 t.Errorf("Flow state not updated, seq number mismatch. got %d want %d.", rxFS2.seq, msg.Seq()) 311 } 312 if res.Success || res.LostCount != 1 || res.Delayed { 313 t.Errorf("Success, lostCount, delayed mismatch. got (%v %v %v) want (%v %v %v)", 314 res.Success, res.LostCount, res.Delayed, false, 1, false) 315 } 316 } 317 318 // TestMessagePayloadHandling tests payload handling of the messages. 319 func TestMessagePayloadHandling(t *testing.T) { 320 txFSM := NewFlowStateMap() 321 322 testPayload := "cloudprober" 323 324 src := "aa-src" 325 dst := "zz-dst" 326 seq := uint64(100) 327 ts := time.Now().Truncate(time.Microsecond) 328 329 txFS := txFSM.FlowState(src, "", dst) 330 txFS.seq = seq - 1 331 msgBytes, msgSeq := createMessageWithPayload(t, txFS, ts, []byte(testPayload)) 332 if msgSeq != seq { 333 t.Errorf("Incorrect seq in message: got %d want %d", msgSeq, seq) 334 } 335 336 msg, err := NewMessage(msgBytes) 337 if err != nil { 338 t.Fatalf("Process message failure: %v", err) 339 } 340 if msg.Src() != src || msg.Dst() != dst { 341 t.Errorf("Message content error (src, dst): got (%s, %s) want (%s, %s)", msg.Src(), msg.Dst(), src, dst) 342 } 343 if string(msg.Payload()) != testPayload { 344 t.Errorf("Message payload=%s, want=%s", string(msg.Payload()), testPayload) 345 } 346 }