123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- // Copyright (C) 2023 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- package model
- import (
- "context"
- "strings"
- "testing"
- "github.com/syncthing/syncthing/lib/events"
- "github.com/thejerf/suture/v4"
- )
- func TestServiceMap(t *testing.T) {
- t.Parallel()
- ctx, cancel := context.WithCancel(context.Background())
- t.Cleanup(cancel)
- sup := suture.NewSimple("TestServiceMap")
- sup.ServeBackground(ctx)
- t.Run("SimpleAddRemove", func(t *testing.T) {
- t.Parallel()
- sm := newServiceMap[string, *dummyService](events.NoopLogger)
- sup.Add(sm)
- // Add two services. They should start.
- d1 := newDummyService()
- d2 := newDummyService()
- sm.Add("d1", d1)
- sm.Add("d2", d2)
- <-d1.started
- <-d2.started
- // Remove them. They should stop.
- if !sm.Remove("d1") {
- t.Errorf("Remove failed")
- }
- if !sm.Remove("d2") {
- t.Errorf("Remove failed")
- }
- <-d1.stopped
- <-d2.stopped
- })
- t.Run("OverwriteImpliesRemove", func(t *testing.T) {
- t.Parallel()
- sm := newServiceMap[string, *dummyService](events.NoopLogger)
- sup.Add(sm)
- d1 := newDummyService()
- d2 := newDummyService()
- // Add d1, it should start.
- sm.Add("k", d1)
- <-d1.started
- // Add d2, with the same key. The previous one should stop as we're
- // doing a replace.
- sm.Add("k", d2)
- <-d1.stopped
- <-d2.started
- if !sm.Remove("k") {
- t.Errorf("Remove failed")
- }
- <-d2.stopped
- })
- t.Run("IterateWithRemoveAndWait", func(t *testing.T) {
- t.Parallel()
- sm := newServiceMap[string, *dummyService](events.NoopLogger)
- sup.Add(sm)
- // Add four services.
- d1 := newDummyService()
- d2 := newDummyService()
- d3 := newDummyService()
- d4 := newDummyService()
- sm.Add("keep1", d1)
- sm.Add("remove2", d2)
- sm.Add("keep3", d3)
- sm.Add("remove4", d4)
- <-d1.started
- <-d2.started
- <-d3.started
- <-d4.started
- // Remove two of them from within the iterator.
- sm.Each(func(k string, v *dummyService) error {
- if strings.HasPrefix(k, "remove") {
- sm.RemoveAndWait(k, 0)
- }
- return nil
- })
- // They should have stopped.
- <-d2.stopped
- <-d4.stopped
- // They should not be in the map anymore.
- if _, ok := sm.Get("remove2"); ok {
- t.Errorf("Service still in map")
- }
- if _, ok := sm.Get("remove4"); ok {
- t.Errorf("Service still in map")
- }
- // The other two should still be running.
- if _, ok := sm.Get("keep1"); !ok {
- t.Errorf("Service not in map")
- }
- if _, ok := sm.Get("keep3"); !ok {
- t.Errorf("Service not in map")
- }
- })
- }
- type dummyService struct {
- started chan struct{}
- stopped chan struct{}
- }
- func newDummyService() *dummyService {
- return &dummyService{
- started: make(chan struct{}),
- stopped: make(chan struct{}),
- }
- }
- func (d *dummyService) Serve(ctx context.Context) error {
- close(d.started)
- defer close(d.stopped)
- <-ctx.Done()
- return nil
- }
|