lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/xcontext/xcontext_test.go (about) 1 // Copyright (C) 2017-2018 Nexedi SA and Contributors. 2 // Kirill Smelkov <kirr@nexedi.com> 3 // 4 // This program is free software: you can Use, Study, Modify and Redistribute 5 // it under the terms of the GNU General Public License version 3, or (at your 6 // option) any later version, as published by the Free Software Foundation. 7 // 8 // You can also Link and Combine this program with other software covered by 9 // the terms of any of the Free Software licenses or any of the Open Source 10 // Initiative approved licenses and Convey the resulting work. Corresponding 11 // source of such a combination shall include the source code for all other 12 // software used. 13 // 14 // This program is distributed WITHOUT ANY WARRANTY; without even the implied 15 // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 // 17 // See COPYING file for full licensing terms. 18 // See https://www.nexedi.com/licensing for rationale and options. 19 20 // Package xcontext provides addons to std package context. 21 package xcontext 22 23 import ( 24 "context" 25 "testing" 26 "time" 27 ) 28 29 func TestMerge(t *testing.T) { 30 bg := context.Background() 31 ctx1, cancel1 := context.WithCancel(bg) 32 ctx2, cancel2 := context.WithCancel(bg) 33 34 ctx1 = context.WithValue(ctx1, 1, "hello") 35 ctx2 = context.WithValue(ctx2, 2, "world") 36 37 mc, __ := Merge(ctx1, ctx2); defer __() 38 39 assertEq := func(a, b interface{}) { 40 t.Helper() 41 if a != b { 42 t.Fatalf("%v != %v", a, b) 43 } 44 } 45 46 assertEq(mc.Value(1), "hello") 47 assertEq(mc.Value(2), "world") 48 assertEq(mc.Value(3), nil) 49 50 t0 := time.Time{} 51 52 d, ok := mc.Deadline() 53 if !(d == t0 && ok == false) { 54 t.Fatal("deadline must be unset") 55 } 56 57 assertEq(mc.Err(), nil) 58 59 select { 60 case <-mc.Done(): 61 t.Fatal("done before any parent done") 62 default: 63 } 64 65 cancel2() 66 <-mc.Done() 67 assertEq(mc.Err(), context.Canceled) 68 69 //////// 70 mc, __ = Merge(ctx1, bg); defer __() 71 assertEq(mc.Value(1), "hello") 72 assertEq(mc.Value(2), nil) 73 assertEq(mc.Value(3), nil) 74 75 d, ok = mc.Deadline() 76 if !(d == t0 && ok == false) { 77 t.Fatal("deadline must be unset") 78 } 79 80 assertEq(mc.Err(), nil) 81 82 select { 83 case <-mc.Done(): 84 t.Fatal("done before any parent done") 85 default: 86 } 87 88 cancel1() 89 <-mc.Done() 90 assertEq(mc.Err(), context.Canceled) 91 92 //////// 93 ctx1, cancel1 = context.WithCancel(bg) 94 ctx1 = context.WithValue(ctx1, 3, "zzz") 95 done2 := make(chan struct{}) 96 mc, __ = MergeChan(ctx1, done2); defer __() 97 98 assertEq(mc.Value(1), nil) 99 assertEq(mc.Value(2), nil) 100 assertEq(mc.Value(3), "zzz") 101 102 d, ok = mc.Deadline() 103 if !(d == t0 && ok == false) { 104 t.Fatal("deadline must be unset") 105 } 106 107 assertEq(mc.Err(), nil) 108 109 select { 110 case <-mc.Done(): 111 t.Fatal("done before any parent done") 112 default: 113 } 114 115 close(done2) 116 <-mc.Done() 117 assertEq(mc.Err(), context.Canceled) 118 119 done2 = make(chan struct{}) 120 mc, __ = MergeChan(ctx1, done2); defer __() 121 122 select { 123 case <-mc.Done(): 124 t.Fatal("done before any parent done") 125 default: 126 } 127 128 cancel1() 129 <-mc.Done() 130 assertEq(mc.Err(), context.Canceled) 131 132 //////// 133 t1 := t0.AddDate(7777, 1, 1) 134 t2 := t0.AddDate(9999, 1, 1) 135 ctx1, __ = context.WithDeadline(bg, t1); defer __() 136 ctx2, __ = context.WithDeadline(bg, t2); defer __() 137 138 checkDeadline := func(a, b context.Context, tt time.Time) { 139 t.Helper() 140 m, __ := Merge(a, b); defer __() 141 d, ok := m.Deadline() 142 if !ok { 143 t.Fatal("no deadline returned") 144 } 145 if d != tt { 146 t.Fatalf("incorrect deadline: %v ; want %v", d, tt) 147 } 148 } 149 150 checkDeadline(ctx1, bg, t1) 151 checkDeadline(bg, ctx2, t2) 152 checkDeadline(ctx1, ctx2, t1) 153 checkDeadline(ctx2, ctx1, t1) 154 155 //////// 156 mc, mcancel := Merge(bg, bg) 157 158 select { 159 case <-mc.Done(): 160 t.Fatal("done before any parent done") 161 default: 162 } 163 164 mcancel() 165 mcancel() 166 <-mc.Done() 167 assertEq(mc.Err(), context.Canceled) 168 169 //////// .Err latency (note .wait it not spawned - Err polls sources itself) 170 ctx1, cancel1 = context.WithCancel(bg) 171 ctx2, __ = context.WithCancel(bg); defer __() 172 mc = mergeNoWait(ctx1, ctx2) 173 174 assertEq(ctx1.Err(), nil) 175 assertEq(ctx2.Err(), nil) 176 assertEq(mc.Err(), nil) 177 178 cancel1() 179 assertEq(ctx1.Err(), context.Canceled) 180 assertEq(ctx2.Err(), nil) 181 assertEq(mc.Err(), context.Canceled) 182 183 184 ctx1, __ = context.WithCancel(bg); defer __() 185 ctx2, cancel2 = context.WithCancel(bg) 186 mc = mergeNoWait(ctx1, ctx2) 187 188 assertEq(ctx1.Err(), nil) 189 assertEq(ctx2.Err(), nil) 190 assertEq(mc.Err(), nil) 191 192 cancel2() 193 assertEq(ctx1.Err(), nil) 194 assertEq(ctx2.Err(), context.Canceled) 195 assertEq(mc.Err(), context.Canceled) 196 197 198 mm := mergeNoWait(bg, bg) 199 assertEq(bg.Err(), nil) 200 assertEq(mm.Err(), nil) 201 202 mm.cancel() 203 assertEq(bg.Err(), nil) 204 assertEq(mm.Err(), context.Canceled) 205 } 206 207 // mergeNoWait prepares mergeCtx as Merge would do, but does not spawn its wait. 208 // 209 // useful to check Err latency behaviour. 210 func mergeNoWait(parent1, parent2 context.Context) *mergeCtx { 211 return &mergeCtx{ 212 parent1: parent1, 213 parent2: parent2, 214 done: make(chan struct{}), 215 cancelCh: make(chan struct{}), 216 } 217 }