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  }