@@ -22,6 +22,41 @@ import (
2222 "github.com/calyptia/plugin/metric"
2323)
2424
25+ // TestMain wraps all tests to clean up any calls that mutated globals via public FLB* functions.
26+ func TestMain (m * testing.M ) {
27+ cleanupDone := make (chan struct {})
28+ defer func () {
29+ // Cleanup in a goroutine since buggy plugins or code might indefinitely block
30+ go func () {
31+ defer close (cleanupDone )
32+
33+ setupInstanceForTesting = nil
34+
35+ currInstanceMu .Lock ()
36+ defer currInstanceMu .Unlock ()
37+
38+ if currInstance != nil {
39+ if currInstance .meta .output != nil {
40+ FLBPluginOutputPreExit ()
41+ }
42+ FLBPluginExit ()
43+ }
44+ if pluginMeta .Load () != nil {
45+ pluginMeta .Store (nil )
46+ }
47+ }()
48+
49+ // Ensure cleanup finished
50+ select {
51+ case <- cleanupDone :
52+ case <- time .After (2 * time .Second ):
53+ panic ("timed out cleaning up global plugin instance" )
54+ }
55+ }()
56+
57+ m .Run ()
58+ }
59+
2560func newTestInputInstance (t testing.TB , input InputPlugin ) * pluginInstance {
2661 inst := pluginInstanceWithFakes (newPluginInstance (pluginMetadata {
2762 name : "test-plugin" ,
@@ -78,7 +113,7 @@ func TestInputCallbackLifecycle(t *testing.T) {
78113 require .Equal (t , int64 (1 ), plugin .initCount .Load (), "initialization should only run once" )
79114
80115 // Early attempt to callback
81- _ , callbackResp := testCallback (inst )
116+ _ , callbackResp := testCallback (inst . inputCallback )
82117 require .Equal (t , input .FLB_RETRY , callbackResp , "pre-run must be called before callback" )
83118
84119 // Pre-run
@@ -93,7 +128,7 @@ func TestInputCallbackLifecycle(t *testing.T) {
93128 require .ErrorContains (t , inst .resume (), `invalid plugin state "runnable"` )
94129
95130 // Callback
96- callbackBytes , callbackResp := testCallback (inst )
131+ callbackBytes , callbackResp := testCallback (inst . inputCallback )
97132 require .Equal (t , input .FLB_OK , callbackResp )
98133 require .Equal (t , []Message {m1 , m2 }, decodeMessages (t , callbackBytes ))
99134 require .True (t , plugin .collectRunning .Load ())
@@ -108,21 +143,21 @@ func TestInputCallbackLifecycle(t *testing.T) {
108143 require .False (t , plugin .collectRunning .Load ())
109144 require .NoError (t , inst .stop (), "stop should be idempotent" )
110145
111- callbackBytes , callbackResp = testCallback (inst )
146+ callbackBytes , callbackResp = testCallback (inst . inputCallback )
112147 require .Equal (t , input .FLB_RETRY , callbackResp )
113148 assert .Empty (t , callbackBytes )
114149
115150 // Resume stopped pipeline
116151 require .NoError (t , inst .resume ())
117152 require .ErrorContains (t , inst .resume (), `invalid plugin state "runnable"` )
118- callbackBytes , callbackResp = testCallback (inst )
153+ callbackBytes , callbackResp = testCallback (inst . inputCallback )
119154 require .Equal (t , input .FLB_OK , callbackResp )
120155 assert .Empty (t , callbackBytes , "m3 message from earlier not dequeued" )
121156 require .Eventually (t , plugin .collectRunning .Load , time .Second , time .Millisecond ,
122157 "collect background loop should have started running" )
123158 m4 := testMessage (map [string ]any {"name" : "m4" })
124159 plugin .enqueue (m4 )()
125- callbackBytes , callbackResp = testCallback (inst )
160+ callbackBytes , callbackResp = testCallback (inst . inputCallback )
126161 require .Equal (t , input .FLB_OK , callbackResp )
127162 require .Equal (t , []Message {m4 }, decodeMessages (t , callbackBytes ))
128163
@@ -131,6 +166,70 @@ func TestInputCallbackLifecycle(t *testing.T) {
131166 require .False (t , plugin .collectRunning .Load ())
132167}
133168
169+ // TestGlobalCallbacks is a simplified variant of TestInputCallbackLifecycle that uses the
170+ // C callback functions invoked by fluent-bit.
171+ func TestGlobalCallbacks (t * testing.T ) {
172+ plugin := newTestInputPlugin ()
173+
174+ // Registration
175+ RegisterInput ("test-name" , "test-desc" , plugin )
176+ FLBPluginRegister (unsafe .Pointer (& input.FLBPluginProxyDef {}))
177+
178+ require .Equal (t , & pluginMetadata {
179+ name : "test-name" ,
180+ desc : "test-desc" ,
181+ input : plugin ,
182+ }, pluginMeta .Load ())
183+
184+ // Initialization
185+ setupInstanceForTesting = func (inst * pluginInstance ) {
186+ pluginInstanceWithFakes (inst )
187+ }
188+ FLBPluginInit (nil )
189+ require .Equal (t , int64 (1 ), plugin .initCount .Load ())
190+
191+ currInstanceMu .Lock ()
192+ inst := currInstance
193+ currInstanceMu .Unlock ()
194+ require .NotNil (t , inst )
195+
196+ // Pre-run
197+ FLBPluginInputPreRun (0 )
198+ require .Eventually (t , plugin .collectRunning .Load , time .Second , time .Millisecond ,
199+ "collect background loop should have started running" )
200+ m1 := testMessage (map [string ]any {"name" : "m1" })
201+ plugin .enqueue (m1 )()
202+
203+ // Callback
204+ callbackBytes , callbackResp := testCallback (FLBPluginInputCallback )
205+ require .Equal (t , input .FLB_OK , callbackResp )
206+ require .Equal (t , []Message {m1 }, decodeMessages (t , callbackBytes ))
207+ require .True (t , plugin .collectRunning .Load ())
208+
209+ // Pause
210+ FLBPluginInputPause ()
211+ require .False (t , plugin .collectRunning .Load ())
212+ FLBPluginInputPause () // Idempotent
213+
214+ callbackBytes , callbackResp = testCallback (FLBPluginInputCallback )
215+ require .Equal (t , input .FLB_RETRY , callbackResp )
216+ assert .Empty (t , callbackBytes )
217+
218+ // Resume stopped pipeline
219+ FLBPluginInputResume ()
220+ callbackBytes , callbackResp = testCallback (FLBPluginInputCallback )
221+ require .Equal (t , input .FLB_OK , callbackResp )
222+ m4 := testMessage (map [string ]any {"name" : "m4" })
223+ plugin .enqueue (m4 )()
224+ callbackBytes , callbackResp = testCallback (FLBPluginInputCallback )
225+ require .Equal (t , input .FLB_OK , callbackResp )
226+ require .Equal (t , []Message {m4 }, decodeMessages (t , callbackBytes ))
227+
228+ // Stop again
229+ FLBPluginExit ()
230+ require .False (t , plugin .collectRunning .Load ())
231+ }
232+
134233// testMessage returns a Message with the given record map and current timestamp.
135234func testMessage (record map [string ]any ) Message {
136235 tag := ""
@@ -232,7 +331,7 @@ func TestInputCallbackCtrlC(t *testing.T) {
232331 timeout := time .After (1 * time .Second )
233332
234333 go func () {
235- testCallback (inst )
334+ testCallback (inst . inputCallback )
236335 close (cdone )
237336 }()
238337
@@ -281,11 +380,11 @@ func TestInputCallbackDangle(t *testing.T) {
281380 ticker := time .NewTicker (collectInterval )
282381 defer ticker .Stop ()
283382
284- testCallback (inst )
383+ testCallback (inst . inputCallback )
285384 for {
286385 select {
287386 case <- ticker .C :
288- testCallback (inst )
387+ testCallback (inst . inputCallback )
289388 case <- cdone :
290389 return
291390 }
@@ -349,7 +448,7 @@ func TestInputCallbackInfinite(t *testing.T) {
349448 for {
350449 select {
351450 case <- ticker .C :
352- testCallback (inst )
451+ testCallback (inst . inputCallback )
353452 if ptr != nil {
354453 close (cdone )
355454 return
@@ -421,7 +520,7 @@ func TestInputCallbackLatency(t *testing.T) {
421520 ticker := time .NewTicker (collectInterval )
422521 defer ticker .Stop ()
423522
424- buf , _ := testCallback (inst )
523+ buf , _ := testCallback (inst . inputCallback )
425524 if len (buf ) > 0 {
426525 cmsg <- buf
427526 }
@@ -433,7 +532,7 @@ func TestInputCallbackLatency(t *testing.T) {
433532 t .Log ("---- collect done" )
434533 return
435534 case <- ticker .C :
436- buf , _ := testCallback (inst )
535+ buf , _ := testCallback (inst . inputCallback )
437536 if len (buf ) > 0 {
438537 cmsg <- buf
439538 }
@@ -537,13 +636,13 @@ func TestInputCallbackInfiniteConcurrent(t *testing.T) {
537636 ticker := time .NewTicker (time .Second * 1 )
538637 defer ticker .Stop ()
539638
540- testCallback (inst )
639+ testCallback (inst . inputCallback )
541640 close (cstarted )
542641
543642 for {
544643 select {
545644 case <- ticker .C :
546- testCallback (inst )
645+ testCallback (inst . inputCallback )
547646 case <- inst .runCtx .Done ():
548647 return
549648 }
0 commit comments