github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/systests/sys_test.go (about)

     1  /*
     2  Copyright 2016 Stanislav Liberman
     3  Copyright 2022 Steven Stern
     4  
     5  Licensed under the Apache License, Version 2.0 (the "License");
     6  you may not use this file except in compliance with the License.
     7  You may obtain a copy of the License at
     8  
     9  http://www.apache.org/licenses/LICENSE-2.0
    10  
    11  Unless required by applicable law or agreed to in writing, software
    12  distributed under the License is distributed on an "AS IS" BASIS,
    13  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  See the License for the specific language governing permissions and
    15  limitations under the License.
    16  */
    17  
    18  package systests
    19  
    20  import (
    21  	"flag"
    22  	"fmt"
    23  	"github.com/lirm/aeron-go/systests/driver"
    24  	"github.com/stretchr/testify/suite"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/lirm/aeron-go/aeron"
    29  	"github.com/lirm/aeron-go/aeron/atomic"
    30  	"github.com/lirm/aeron-go/aeron/logbuffer"
    31  	"github.com/lirm/aeron-go/aeron/logging"
    32  )
    33  
    34  var ExamplesConfig = struct {
    35  	TestChannel  *string
    36  	TestStreamID *int
    37  	LoggingOn    *bool
    38  }{
    39  	flag.String("c", "aeron:ipc", "test channel"),
    40  	flag.Int("s", 10, "streamId to use"),
    41  	flag.Bool("l", false, "enable logging"),
    42  }
    43  
    44  var logger = logging.MustGetLogger("systests")
    45  
    46  type SysTestSuite struct {
    47  	suite.Suite
    48  	mediaDriver *driver.MediaDriver
    49  }
    50  
    51  func (suite *SysTestSuite) SetupTest() {
    52  	mediaDriver, err := driver.StartMediaDriver()
    53  	suite.Require().NoError(err, "Couldn't start Media Driver: ")
    54  	suite.mediaDriver = mediaDriver
    55  }
    56  
    57  func (suite *SysTestSuite) TearDownTest() {
    58  	suite.mediaDriver.StopMediaDriver()
    59  }
    60  
    61  func (suite *SysTestSuite) send(n int, pub *aeron.Publication) {
    62  	message := "this is a message"
    63  	srcBuffer := atomic.MakeBuffer(([]byte)(message))
    64  
    65  	for i := 0; i < n; i++ {
    66  		timeoutAt := time.Now().Add(time.Second * 5)
    67  		var v int64
    68  		for v <= 0 {
    69  			v = pub.Offer(srcBuffer, 0, int32(len(message)), nil)
    70  			if time.Now().After(timeoutAt) {
    71  				suite.Fail("Send timed out")
    72  			}
    73  			time.Sleep(time.Millisecond)
    74  		}
    75  	}
    76  }
    77  
    78  func (suite *SysTestSuite) receive(n int, sub *aeron.Subscription) {
    79  	counter := 0
    80  	handler := func(buffer *atomic.Buffer, offset int32, length int32, header *logbuffer.Header) {
    81  		logger.Debugf("    message: %s", string(buffer.GetBytesArray(offset, length)))
    82  		counter++
    83  	}
    84  	var fragmentsRead atomic.Int
    85  	for int(fragmentsRead.Get()) < n {
    86  		timeoutAt := time.Now().Add(time.Second)
    87  		for {
    88  			recvd := sub.Poll(handler, 10)
    89  			if recvd >= 1 {
    90  				fragmentsRead.Add(int32(recvd))
    91  				logger.Debugf("  have %d fragments", fragmentsRead)
    92  				break
    93  			}
    94  			if time.Now().After(timeoutAt) {
    95  				suite.Fail("Receive timed out")
    96  			}
    97  			time.Sleep(time.Millisecond)
    98  		}
    99  	}
   100  	suite.Assert().EqualValues(fragmentsRead.Get(), n)
   101  	suite.Assert().EqualValues(counter, n)
   102  }
   103  
   104  func (suite *SysTestSuite) subAndSend(n int, a *aeron.Aeron, pub *aeron.Publication) {
   105  	sub, err := a.AddSubscription(*ExamplesConfig.TestChannel, int32(*ExamplesConfig.TestStreamID))
   106  	suite.Require().NoError(err)
   107  	defer sub.Close()
   108  
   109  	// This is basically a requirement since we need to wait
   110  	for !aeron.IsConnectedTo(sub, pub) {
   111  		time.Sleep(time.Millisecond)
   112  	}
   113  
   114  	suite.send(n, pub)
   115  	suite.receive(n, sub)
   116  }
   117  
   118  func logtest(flag bool) {
   119  	fmt.Printf("Logging: %t\n", flag)
   120  	if flag {
   121  		logging.SetLevel(logging.DEBUG, "aeron")
   122  		logging.SetLevel(logging.DEBUG, "memmap")
   123  		logging.SetLevel(logging.DEBUG, "driver")
   124  		logging.SetLevel(logging.DEBUG, "counters")
   125  		logging.SetLevel(logging.DEBUG, "logbuffers")
   126  		logging.SetLevel(logging.DEBUG, "buffer")
   127  	} else {
   128  		logging.SetLevel(logging.INFO, "aeron")
   129  		logging.SetLevel(logging.INFO, "memmap")
   130  		logging.SetLevel(logging.INFO, "driver")
   131  		logging.SetLevel(logging.INFO, "counters")
   132  		logging.SetLevel(logging.INFO, "logbuffers")
   133  		logging.SetLevel(logging.INFO, "buffer")
   134  
   135  	}
   136  }
   137  
   138  // TestAeronBasics will check for a simple send/receive scenario.
   139  func (suite *SysTestSuite) TestAeronBasics() {
   140  	logger.Debug("Started TestAeronBasics")
   141  
   142  	a, err := aeron.Connect(aeron.NewContext().AeronDir(suite.mediaDriver.TempDir))
   143  	if err != nil {
   144  		suite.Failf("Failed to connect to driver: %s", err.Error())
   145  	}
   146  	defer a.Close()
   147  
   148  	pub, err := a.AddPublication(*ExamplesConfig.TestChannel, int32(*ExamplesConfig.TestStreamID))
   149  	suite.Require().NoError(err)
   150  	defer pub.Close()
   151  	//logger.Debugf("Added publication: %v\n", pub)
   152  
   153  	suite.subAndSend(1, a, pub)
   154  }
   155  
   156  // TestAeronSendMultipleMessages tests sending and receive multiple messages in a row.
   157  func (suite *SysTestSuite) TestAeronSendMultipleMessages() {
   158  	logger.Debug("Started TestAeronSendMultipleMessages")
   159  
   160  	a, err := aeron.Connect(aeron.NewContext().AeronDir(suite.mediaDriver.TempDir))
   161  	suite.Require().Nil(err, "Failed to connect to driver: %s", err)
   162  	defer a.Close()
   163  
   164  	for i := 0; i < 3; i++ {
   165  		logger.Debugf("NextCorrelationID = %d", a.NextCorrelationID())
   166  	}
   167  	suite.Require().NotEqual(a.NextCorrelationID(), 0, "invalid zero NextCorrelationID")
   168  
   169  	pub, err := a.AddPublication(*ExamplesConfig.TestChannel, int32(*ExamplesConfig.TestStreamID))
   170  	suite.Require().NoError(err)
   171  	defer pub.Close()
   172  
   173  	sub, err := a.AddSubscription(*ExamplesConfig.TestChannel, int32(*ExamplesConfig.TestStreamID))
   174  	suite.Require().NoError(err)
   175  	defer sub.Close()
   176  
   177  	// This is basically a requirement since we need to wait
   178  	for !aeron.IsConnectedTo(sub, pub) {
   179  		time.Sleep(time.Millisecond)
   180  	}
   181  
   182  	itCount := 100
   183  	go suite.send(itCount, pub)
   184  	suite.receive(itCount, sub)
   185  }
   186  
   187  // TestAeronSendMultiplePublications tests sending on multiple publications with a sigle
   188  // subscription receiving. In IPC local mode this will end up with using the same Publication
   189  // but it's a scenario nonetheless. As all systests this assumes a running media driver.
   190  func (suite *SysTestSuite) NotTestedYet_TestAeronSendMultiplePublications() {
   191  	logger.Debug("Started TestAeronSendMultiplePublications")
   192  
   193  	//go func() {
   194  	//	sigs := make(chan os.Signal, 1)
   195  	//	signal.Notify(sigs, syscall.SIGQUIT)
   196  	//	buf := make([]byte, 1<<20)
   197  	//	for {
   198  	//		<-sigs
   199  	//		stacklen := runtime.Stack(buf, true)
   200  	//		log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen])
   201  	//	}
   202  	//}()
   203  
   204  	a, err := aeron.Connect(aeron.NewContext())
   205  	if err != nil {
   206  		logger.Fatalf("Failed to connect to driver: %s\n", err.Error())
   207  	}
   208  	defer a.Close()
   209  
   210  	pubCount := 10
   211  	itCount := 100
   212  
   213  	sub, err := a.AddSubscription(*ExamplesConfig.TestChannel, int32(*ExamplesConfig.TestStreamID))
   214  	suite.Require().NoError(err)
   215  	defer sub.Close()
   216  
   217  	pubs := make([]*aeron.Publication, pubCount)
   218  
   219  	for i := 0; i < pubCount; i++ {
   220  		pub, err := a.AddPublication(*ExamplesConfig.TestChannel, int32(*ExamplesConfig.TestStreamID))
   221  		suite.Require().NoError(err)
   222  		defer pub.Close()
   223  
   224  		pubs[i] = pub
   225  
   226  		// This is basically a requirement since we need to wait
   227  		for !aeron.IsConnectedTo(sub, pub) {
   228  			time.Sleep(time.Millisecond)
   229  		}
   230  	}
   231  
   232  	logger.Debugf(" ==> Got pubs %v", pubs)
   233  
   234  	go suite.receive(itCount*pubCount, sub)
   235  
   236  	time.Sleep(200 * time.Millisecond)
   237  
   238  	// Send
   239  	for i := 0; i < itCount; i++ {
   240  		for pIx, p := range pubs {
   241  			suite.send(1, p)
   242  			logger.Debugf("sent %d to pubs[%d]", i, pIx)
   243  			logger.Debugf("sent %d to pubs[%d]", i, pIx)
   244  		}
   245  	}
   246  
   247  }
   248  
   249  // TestAeronResubscribe test using different subscriptions with the same publication
   250  func (suite *SysTestSuite) NotTestedYet_TestAeronResubscribe() {
   251  	logger.Debug("Started TestAeronResubscribe")
   252  
   253  	a, err := aeron.Connect(aeron.NewContext())
   254  	suite.Require().NoError(err)
   255  	defer a.Close()
   256  
   257  	pub, err := a.AddPublication(*ExamplesConfig.TestChannel, int32(*ExamplesConfig.TestStreamID))
   258  	suite.Require().NoError(err)
   259  
   260  	suite.subAndSend(1, a, pub)
   261  	suite.subAndSend(1, a, pub)
   262  }
   263  
   264  // TestResubStress tests sending and receiving when creating a new subscription for each cycle
   265  func (suite *SysTestSuite) NotTestedYet_TestResubStress() {
   266  	logger.Debug("Started TestAeronResubscribe")
   267  
   268  	a, err := aeron.Connect(aeron.NewContext())
   269  	suite.Require().NoError(err)
   270  	defer a.Close()
   271  
   272  	pub, err := a.AddPublication(*ExamplesConfig.TestChannel, int32(*ExamplesConfig.TestStreamID))
   273  	suite.Require().NoError(err)
   274  	for i := 0; i < 100; i++ {
   275  		suite.subAndSend(1, a, pub)
   276  		logger.Debugf("bounce %d", i)
   277  	}
   278  }
   279  
   280  // TestAeronClose simply tests explicit call to ctx.Close()
   281  func testAeronClose() {
   282  	logger.Debug("Started TestAeronClose")
   283  
   284  	ctx := aeron.NewContext().MediaDriverTimeout(time.Second * 5)
   285  	a, err := aeron.Connect(ctx)
   286  	if err != nil {
   287  		logger.Fatalf("Failed to connect to driver: %s\n", err.Error())
   288  	}
   289  	a.Close()
   290  }
   291  
   292  func TestSuiteMain(t *testing.T) {
   293  	flag.Parse()
   294  	logtest(*ExamplesConfig.LoggingOn)
   295  	suite.Run(t, new(SysTestSuite))
   296  
   297  	//testAeronClose()
   298  
   299  	//testAeronResubscribe()
   300  
   301  	//testAeronSendMultiplePublications()
   302  
   303  	//testResubStress()
   304  }