blob: f5cc592d6f4012e63479256e0e1db885b19981ca [file] [log] [blame]
vinokumaf7605fc2023-06-02 18:08:01 +05301// Copyright 2011 Google Inc.
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
15package gomock
16
17import (
18 "bytes"
Abhay Kumarfe505f22025-11-10 14:16:31 +000019 "errors"
vinokumaf7605fc2023-06-02 18:08:01 +053020 "fmt"
bseenivadd66c362026-02-12 19:13:26 +053021 "sync"
vinokumaf7605fc2023-06-02 18:08:01 +053022)
23
24// callSet represents a set of expected calls, indexed by receiver and method
25// name.
26type callSet struct {
27 // Calls that are still expected.
bseenivadd66c362026-02-12 19:13:26 +053028 expected map[callSetKey][]*Call
29 expectedMu *sync.Mutex
vinokumaf7605fc2023-06-02 18:08:01 +053030 // Calls that have been exhausted.
31 exhausted map[callSetKey][]*Call
bseenivadd66c362026-02-12 19:13:26 +053032 // when set to true, existing call expectations are overridden when new call expectations are made
33 allowOverride bool
vinokumaf7605fc2023-06-02 18:08:01 +053034}
35
36// callSetKey is the key in the maps in callSet
37type callSetKey struct {
bseenivadd66c362026-02-12 19:13:26 +053038 receiver any
vinokumaf7605fc2023-06-02 18:08:01 +053039 fname string
40}
41
42func newCallSet() *callSet {
bseenivadd66c362026-02-12 19:13:26 +053043 return &callSet{
44 expected: make(map[callSetKey][]*Call),
45 expectedMu: &sync.Mutex{},
46 exhausted: make(map[callSetKey][]*Call),
47 }
48}
49
50func newOverridableCallSet() *callSet {
51 return &callSet{
52 expected: make(map[callSetKey][]*Call),
53 expectedMu: &sync.Mutex{},
54 exhausted: make(map[callSetKey][]*Call),
55 allowOverride: true,
56 }
vinokumaf7605fc2023-06-02 18:08:01 +053057}
58
59// Add adds a new expected call.
60func (cs callSet) Add(call *Call) {
61 key := callSetKey{call.receiver, call.method}
bseenivadd66c362026-02-12 19:13:26 +053062
63 cs.expectedMu.Lock()
64 defer cs.expectedMu.Unlock()
65
vinokumaf7605fc2023-06-02 18:08:01 +053066 m := cs.expected
67 if call.exhausted() {
68 m = cs.exhausted
69 }
bseenivadd66c362026-02-12 19:13:26 +053070 if cs.allowOverride {
71 m[key] = make([]*Call, 0)
72 }
73
vinokumaf7605fc2023-06-02 18:08:01 +053074 m[key] = append(m[key], call)
75}
76
77// Remove removes an expected call.
78func (cs callSet) Remove(call *Call) {
79 key := callSetKey{call.receiver, call.method}
bseenivadd66c362026-02-12 19:13:26 +053080
81 cs.expectedMu.Lock()
82 defer cs.expectedMu.Unlock()
83
vinokumaf7605fc2023-06-02 18:08:01 +053084 calls := cs.expected[key]
85 for i, c := range calls {
86 if c == call {
87 // maintain order for remaining calls
88 cs.expected[key] = append(calls[:i], calls[i+1:]...)
89 cs.exhausted[key] = append(cs.exhausted[key], call)
90 break
91 }
92 }
93}
94
95// FindMatch searches for a matching call. Returns error with explanation message if no call matched.
bseenivadd66c362026-02-12 19:13:26 +053096func (cs callSet) FindMatch(receiver any, method string, args []any) (*Call, error) {
vinokumaf7605fc2023-06-02 18:08:01 +053097 key := callSetKey{receiver, method}
98
bseenivadd66c362026-02-12 19:13:26 +053099 cs.expectedMu.Lock()
100 defer cs.expectedMu.Unlock()
101
vinokumaf7605fc2023-06-02 18:08:01 +0530102 // Search through the expected calls.
103 expected := cs.expected[key]
104 var callsErrors bytes.Buffer
105 for _, call := range expected {
106 err := call.matches(args)
107 if err != nil {
108 _, _ = fmt.Fprintf(&callsErrors, "\n%v", err)
109 } else {
110 return call, nil
111 }
112 }
113
114 // If we haven't found a match then search through the exhausted calls so we
115 // get useful error messages.
116 exhausted := cs.exhausted[key]
117 for _, call := range exhausted {
118 if err := call.matches(args); err != nil {
119 _, _ = fmt.Fprintf(&callsErrors, "\n%v", err)
Abhay Kumarfe505f22025-11-10 14:16:31 +0000120 continue
vinokumaf7605fc2023-06-02 18:08:01 +0530121 }
Abhay Kumarfe505f22025-11-10 14:16:31 +0000122 _, _ = fmt.Fprintf(
123 &callsErrors, "all expected calls for method %q have been exhausted", method,
124 )
vinokumaf7605fc2023-06-02 18:08:01 +0530125 }
126
127 if len(expected)+len(exhausted) == 0 {
128 _, _ = fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method)
129 }
130
Abhay Kumarfe505f22025-11-10 14:16:31 +0000131 return nil, errors.New(callsErrors.String())
vinokumaf7605fc2023-06-02 18:08:01 +0530132}
133
134// Failures returns the calls that are not satisfied.
135func (cs callSet) Failures() []*Call {
bseenivadd66c362026-02-12 19:13:26 +0530136 cs.expectedMu.Lock()
137 defer cs.expectedMu.Unlock()
138
vinokumaf7605fc2023-06-02 18:08:01 +0530139 failures := make([]*Call, 0, len(cs.expected))
140 for _, calls := range cs.expected {
141 for _, call := range calls {
142 if !call.satisfied() {
143 failures = append(failures, call)
144 }
145 }
146 }
147 return failures
148}
bseenivadd66c362026-02-12 19:13:26 +0530149
150// Satisfied returns true in case all expected calls in this callSet are satisfied.
151func (cs callSet) Satisfied() bool {
152 cs.expectedMu.Lock()
153 defer cs.expectedMu.Unlock()
154
155 for _, calls := range cs.expected {
156 for _, call := range calls {
157 if !call.satisfied() {
158 return false
159 }
160 }
161 }
162
163 return true
164}