github.com/swaros/contxt/module/runner@v0.0.0-20240305083542-3dbd4436ac40/testrunner_test.go (about)

     1  package runner_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/swaros/contxt/module/configure"
    15  	"github.com/swaros/contxt/module/ctxout"
    16  	"github.com/swaros/contxt/module/dirhandle"
    17  	"github.com/swaros/contxt/module/runner"
    18  	"github.com/swaros/contxt/module/systools"
    19  	"github.com/swaros/contxt/module/tasks"
    20  	"github.com/swaros/contxt/module/yacl"
    21  	"github.com/swaros/contxt/module/yamc"
    22  )
    23  
    24  var useLastDir = "./"
    25  var lastExistCode = 0
    26  var testDirectory = ""
    27  
    28  type ExpectDef struct {
    29  	ExpectedInOutput []string "yaml:\"output\"" // what should be in the output (contains! not full match)
    30  	ExpectedInError  []string "yaml:\"error\""  // what should be in the error (contains! not full match)
    31  	NotExpected      []string "yaml:\"not\""    // what should not be in the output (contains! not full match)
    32  }
    33  
    34  type TestRunExpectation struct {
    35  	TestName       string    "yaml:\"testName\""       // the nameof the test
    36  	RunCmd         string    "yaml:\"runCmd\""         // the run command to execute
    37  	RunInteractive string    "yaml:\"runInteractive\"" // the run command to execute in the interactive mode
    38  	Folder         string    "yaml:\"folder\""         // the folder where the test is located. empty to use the current directory
    39  	Systems        []string  "yaml:\"systems\""        // what system is ment like linux, windows, darwin etc.
    40  	Expectations   ExpectDef "yaml:\"expect\""
    41  	Disabled       bool      "yaml:\"disabled\"" // if the test is disabled
    42  }
    43  
    44  func TestAllIssues(t *testing.T) {
    45  	// walk on every file in the directory starting from ./testdata/issues
    46  	// and run the test if the file is prefixed with 'issue_'
    47  
    48  	// change to the testdata directory
    49  	path := ChangeToRuntimeDir(t)
    50  
    51  	// walk on every file in the directory
    52  	// look for files prefixed with 'issue_'
    53  	// and run the test
    54  	err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
    55  		if err != nil {
    56  			return err
    57  		}
    58  		// check if we have an file and if the file is prefixed with 'issue_' and has the suffix '.yml'
    59  		// but use the filename for test prefix
    60  
    61  		baseName := filepath.Base(path)
    62  		if !info.IsDir() && strings.HasPrefix(baseName, "issue_") && strings.HasSuffix(path, ".yml") {
    63  
    64  			// load the test definition
    65  			testDef := TestRunExpectation{}
    66  			loader := yacl.New(&testDef, yamc.NewYamlReader()).SetFileAndPathsByFullFilePath(path)
    67  			if err := loader.Load(); err != nil {
    68  				t.Error(err)
    69  			}
    70  			if loader.GetLoadedFile() == "" {
    71  				t.Error("could not load the test definition file: " + path)
    72  			} else {
    73  				if testDef.Disabled {
    74  					t.Log("test " + testDef.TestName + " is disabled")
    75  					return nil
    76  				}
    77  
    78  				if testDef.Systems != nil && len(testDef.Systems) > 0 {
    79  					// check if the current system is in the list of systems
    80  					// if not, we skip the test
    81  					if !systools.StringInSlice(runtime.GOOS, testDef.Systems) {
    82  						return nil
    83  					}
    84  				}
    85  
    86  				testDef.Folder, _ = filepath.Abs(filepath.Dir(path))
    87  				IssueTester(t, testDef)
    88  			}
    89  		}
    90  		return nil
    91  
    92  	})
    93  	if err != nil {
    94  		panic(err)
    95  	}
    96  }
    97  
    98  // IssueTester is the main test function for the issues based of the test definition
    99  // the test definition is loaded from a yaml file.
   100  func IssueTester(t *testing.T, testDef TestRunExpectation) error {
   101  	t.Helper()
   102  	tasks.NewGlobalWatchman().ResetAllTaskInfos()
   103  	ChangeToRuntimeDir(t)
   104  	app, output, appErr := SetupTestApp("issues", "ctx_test_config.yml")
   105  	if appErr != nil {
   106  		t.Errorf("Expected no error, got '%v'", appErr)
   107  		return appErr
   108  	}
   109  	cleanAllFiles()
   110  	defer cleanAllFiles()
   111  
   112  	// set the log file with an timestamp
   113  	logFileName := testDef.TestName + "_" + time.Now().Format(time.RFC3339) + ".log"
   114  	output.SetLogFile(getAbsolutePath(logFileName))
   115  	output.ClearAndLog()
   116  	currentDir := dirhandle.Pushd()
   117  	defer currentDir.Popd()
   118  
   119  	// change into the test directory
   120  	if testDef.Folder != "" {
   121  		if err := os.Chdir(testDef.Folder); err != nil {
   122  			t.Errorf("error by changing the directory. check test %s: '%v'", testDef.TestName, err)
   123  			return err
   124  		}
   125  	}
   126  
   127  	assertSomething := 0
   128  	runSomething := false
   129  
   130  	// command to run a cobracmd.
   131  	if testDef.RunCmd != "" {
   132  		runSomething = true
   133  		if err := runCobraCmd(app, testDef.RunCmd); err != nil {
   134  			if len(testDef.Expectations.ExpectedInError) > 0 {
   135  				for _, expected := range testDef.Expectations.ExpectedInError {
   136  					assertInMessage(t, output, expected)
   137  					assertSomething++
   138  				}
   139  			} else {
   140  				t.Errorf("Expected no error, got '%v'", err)
   141  				return err
   142  			}
   143  		}
   144  	}
   145  
   146  	if testDef.RunInteractive != "" {
   147  		runSomething = true
   148  		if err := runCobraCmdNonSplits(app, testDef.RunInteractive); err != nil {
   149  			if len(testDef.Expectations.ExpectedInError) > 0 {
   150  				for _, expected := range testDef.Expectations.ExpectedInError {
   151  					assertInMessage(t, output, expected)
   152  					assertSomething++
   153  				}
   154  			} else {
   155  				t.Errorf("Expected no error, got '%v'", err)
   156  				return err
   157  			}
   158  		}
   159  	}
   160  
   161  	if !runSomething {
   162  		t.Error("no run command was set. please check the test definition for test " + testDef.TestName)
   163  		return fmt.Errorf("no run command was set. please check the test definition for test " + testDef.TestName)
   164  	}
   165  	t.Log("test " + testDef.TestName + " was executed in: " + testDef.Folder)
   166  	if len(testDef.Expectations.ExpectedInOutput) > 0 {
   167  		for _, expected := range testDef.Expectations.ExpectedInOutput {
   168  			assertInMessage(t, output, expected)
   169  			assertSomething++
   170  		}
   171  	}
   172  
   173  	// looking for the not expected
   174  	if len(testDef.Expectations.NotExpected) > 0 {
   175  		for _, expected := range testDef.Expectations.NotExpected {
   176  			assertNotInMessage(t, output, expected)
   177  			assertSomething++
   178  		}
   179  	}
   180  	output.ClearAndLog()
   181  	if assertSomething == 0 {
   182  		t.Error("no expectations was set and tested. please check the test definition")
   183  		return fmt.Errorf("no expectations was set and tested. please check the test definition")
   184  	}
   185  	return nil
   186  }
   187  
   188  func RuntimeFileInfo(t *testing.T) string {
   189  	_, filename, _, _ := runtime.Caller(0)
   190  	return filename
   191  }
   192  
   193  func ChangeToRuntimeDir(t *testing.T) string {
   194  	_, filename, _, _ := runtime.Caller(0)
   195  	dir := filepath.Dir(filename)
   196  	if err := os.Chdir(dir); err != nil {
   197  		t.Error(err)
   198  	}
   199  	return dir
   200  }
   201  
   202  // shortcut for running a cobra command
   203  // without any other setup
   204  func runCobraCommand(runnCallback func(cobra *runner.SessionCobra, writer io.Writer)) string {
   205  	cobra := runner.NewCobraCmds()
   206  	cmpltn := new(bytes.Buffer)
   207  	if runnCallback != nil {
   208  		runnCallback(cobra, cmpltn)
   209  	}
   210  	return cmpltn.String()
   211  }
   212  
   213  // this are some helper functions especially for testing the runner
   214  // Setup the test app
   215  // create the application. set up the config folder name, and the name of the config file.
   216  // the testapp bevavior is afterwards different, because it uses the config
   217  // related to the current directory.
   218  //
   219  //	if the file should remover automatically, it needs prefixed by 'ctx_'.
   220  //
   221  // thats why we have some special helper functions.
   222  //   - getAbsolutePath to get the absolute path to the testdata directory
   223  //   - backToWorkDir to go back to the testdata directory
   224  //   - cleanAllFiles to remove the config file
   225  func SetupTestApp(dir, file string) (*runner.CmdSession, *TestOutHandler, error) {
   226  	tasks.NewGlobalWatchman().ResetAllTaskInfos()
   227  	file = strings.ReplaceAll(file, ":", "_")
   228  	file = strings.ReplaceAll(file, "-", "_")
   229  	file = strings.ReplaceAll(file, "+", "_")
   230  
   231  	// first we want to catch the exist codes
   232  	systools.AddExitListener("testing_prevent_exit", func(no int) systools.ExitBehavior {
   233  		lastExistCode = no
   234  		return systools.Interrupt
   235  	})
   236  
   237  	configure.USE_SPECIAL_DIR = false   // no special directory like userHome etc.
   238  	configure.CONTXT_FILE = file        // set the configuration file name
   239  	configure.MIGRATION_ENABLED = false // disable the migration
   240  	configure.CONTEXT_DIR = dir         // set the directory name
   241  
   242  	// save the current directory
   243  	// and also get back to them (next time)
   244  	popdTestDir()
   245  	// we need to stick to the testdata directory
   246  	// any other directory will not work
   247  	if err := os.Chdir("./testdata"); err != nil {
   248  
   249  		panic(err)
   250  	}
   251  	// check if the directory exists, that we want to use in the testdata directory.
   252  	// even if the config package is able to create them, we want avoid this here.
   253  	if _, err := os.Stat(dir); os.IsNotExist(err) {
   254  		panic(err.Error() + "| the directory " + dir + " does not exist in the testdata directory")
   255  	}
   256  
   257  	// build the absolute path to the testdata directory
   258  	// this is needed to go back to the testdata directory
   259  	// if needed
   260  	if pwd, derr := os.Getwd(); derr == nil {
   261  		useLastDir = pwd
   262  		configure.CONFIG_PATH_CALLBACK = func() string {
   263  			return useLastDir + "/" + configure.CONTEXT_DIR + "/" + configure.CONTXT_FILE
   264  		}
   265  	} else {
   266  		panic(derr)
   267  	}
   268  
   269  	app := runner.NewCmdSession()
   270  
   271  	// set the TemplateHndl OnLoad function to parse required files
   272  	// like it is done in the real application
   273  	onLoadFn := func(template *configure.RunConfig) error {
   274  		return app.SharedHelper.MergeRequiredPaths(template, app.TemplateHndl)
   275  	}
   276  	app.TemplateHndl.SetOnLoad(onLoadFn)
   277  
   278  	functions := runner.NewCmd(app)
   279  	// init the main functions
   280  	functions.MainInit()
   281  
   282  	// signs filter
   283  	signsFilter := ctxout.NewSignFilter(ctxout.NewBaseSignSet())
   284  	ctxout.AddPostFilter(signsFilter)
   285  	// tabout filter
   286  	tabouOutFilter := ctxout.NewTabOut()
   287  	ctxout.AddPostFilter(tabouOutFilter)
   288  	info := ctxout.PostFilterInfo{
   289  		Width:      800,   // give us a big width so we can render the whole line
   290  		IsTerminal: false, //no terminal
   291  		Colored:    false, // no colors
   292  		Height:     500,   // give us a big height so we can render the whole line
   293  		Disabled:   true,
   294  	}
   295  	tabouOutFilter.Update(info)
   296  	signsFilter.Update(info)
   297  	signsFilter.ForceEmpty(true)
   298  
   299  	if err := app.Cobra.Init(functions); err != nil {
   300  		panic(err)
   301  	}
   302  	ctxout.ForceFilterUpdate(info)
   303  
   304  	outputHdnl := NewTestOutHandler()
   305  	app.OutPutHdnl = outputHdnl
   306  	configure.GetGlobalConfig().ResetConfig()
   307  	return app, outputHdnl, nil
   308  }
   309  
   310  // helper function to verify the configuration file.
   311  // if the testCallBack is not nil, we will call it with the configuration model
   312  // so we can check the content of the configuration file.
   313  // this is helpfull just because to double check the content of the file itself and
   314  // the current state of the configuration. the configuration can be different from the file.
   315  // just because the configuration is in memory and the file is on the disk.
   316  // this functions is all about checking if the configuration is updated correctly, also in the file content.
   317  func verifyConfigurationFile(t *testing.T, testCallBack func(CFG *configure.ConfigMetaV2)) {
   318  	t.Helper()
   319  	file := ""
   320  	if configure.CONFIG_PATH_CALLBACK != nil {
   321  		file = configure.CONFIG_PATH_CALLBACK()
   322  	}
   323  
   324  	if file == "" {
   325  		t.Error("configuration setup failed. could not determine the configuration file.")
   326  		return
   327  	}
   328  
   329  	if _, err := os.Stat(file); os.IsNotExist(err) {
   330  		t.Error("configuration file not found: ", file)
   331  		return
   332  	}
   333  	// if the testCallBack is nil, we dont need to check the content
   334  	if testCallBack == nil {
   335  		return
   336  	}
   337  	// model
   338  	var CFG configure.ConfigMetaV2 = configure.ConfigMetaV2{}
   339  	// load the configuration file
   340  	loader := yacl.New(&CFG, yamc.NewYamlReader()).SetFileAndPathsByFullFilePath(file)
   341  	if err := loader.Load(); err != nil {
   342  		t.Error(err)
   343  	}
   344  	testCallBack(&CFG)
   345  
   346  }
   347  
   348  // save and go back to the test folder
   349  func popdTestDir() {
   350  	// if not set, we get the current directory
   351  	// and set them once.
   352  	// so the carefully use this function in the first place
   353  	if testDirectory == "" {
   354  		if pwd, derr := os.Getwd(); derr == nil {
   355  			testDirectory = pwd
   356  		} else {
   357  			panic(derr)
   358  		}
   359  	}
   360  
   361  	if err := os.Chdir(testDirectory); err != nil {
   362  		panic(err)
   363  	}
   364  }
   365  
   366  // helper function to change back to the testdata directory
   367  func backToWorkDir() {
   368  	if err := os.Chdir(useLastDir); err != nil {
   369  		panic(err)
   370  	}
   371  }
   372  
   373  // helper function to get the absolute path to the testdata directory
   374  func getAbsolutePath(dir string) string {
   375  
   376  	dir = useLastDir + "/" + dir
   377  	dir = filepath.Clean(dir)
   378  	if filepath.IsAbs(dir) {
   379  		return dir
   380  	}
   381  	abs, err := filepath.Abs(dir)
   382  	if err != nil {
   383  		panic(err)
   384  	}
   385  	return abs
   386  }
   387  
   388  // helper function to remove the config files
   389  // from testdata/config folder
   390  func cleanAllFiles() {
   391  	popdTestDir()
   392  	if err := os.Chdir("./testdata/config"); err != nil {
   393  		panic(err)
   394  	}
   395  	// walk on every file in the directory
   396  	// and remove it
   397  	err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
   398  		if err != nil {
   399  			return err
   400  		}
   401  		if !info.IsDir() && strings.HasPrefix(path, "ctx_") && strings.HasSuffix(path, ".yml") {
   402  			return os.Remove(path)
   403  		}
   404  		return nil
   405  
   406  	})
   407  	if err != nil {
   408  		panic(err)
   409  	}
   410  	popdTestDir()
   411  }
   412  
   413  // helper function to run a cobra command by argument line
   414  func runCobraCmd(app *runner.CmdSession, cmd string) error {
   415  	app.Cobra.RootCmd.SetArgs(strings.Split(cmd, " "))
   416  	return app.Cobra.RootCmd.Execute()
   417  }
   418  
   419  // helper function to run a cobra command by argument line
   420  func runCobraCmdNonSplits(app *runner.CmdSession, cmd string) error {
   421  	app.Cobra.RootCmd.SetArgs([]string{cmd})
   422  	return app.Cobra.RootCmd.Execute()
   423  }
   424  
   425  // checks if the given string is part of the output buffer
   426  // if not, it will fail the test.
   427  // the special thing about this function is, that it will split the string
   428  // by new line and check if every line is part of the output buffer.
   429  // example:
   430  //
   431  //	output.ClearAndLog()
   432  //	if err := runCobraCmd(app, "workspace new ducktale"); err != nil {
   433  //	   t.Errorf("Expected no error, got '%v'", err)
   434  //	 }
   435  //	assertInMessage(t, output, "ducktale\ncreated\nproject")
   436  func assertSplitTestInMessage(t *testing.T, output *TestOutHandler, msg string) {
   437  	t.Helper()
   438  	parts := strings.Split(msg, "\n")
   439  	errorHappen := false
   440  	for _, part := range parts {
   441  		if part == "" {
   442  			continue
   443  		}
   444  		if !output.Contains(part) {
   445  			errorHappen = true
   446  			t.Errorf("Expected [%s]not found in the output", part)
   447  		}
   448  	}
   449  	if errorHappen {
   450  		t.Error("this is the source output\n", output.String())
   451  	}
   452  }
   453  
   454  // assert a string is part of the output buffer
   455  func assertInMessage(t *testing.T, output *TestOutHandler, msg string) {
   456  	t.Helper()
   457  	if !output.Contains(msg) {
   458  		t.Errorf("Expected \n%s\n-- but instead we did not found it in --\n%v\n", msg, output.String())
   459  	}
   460  }
   461  
   462  // assert a string is part of the output buffer as regex
   463  func assertRegexmatchInMessage(t *testing.T, output *TestOutHandler, msg string) {
   464  	t.Helper()
   465  	if !output.TestRegexPattern(msg) {
   466  		t.Errorf("Expected \n%s\nbut instead we got\n%v", msg, output.String())
   467  	}
   468  }
   469  
   470  // assert a string is not part of the output buffer
   471  func assertNotInMessage(t *testing.T, output *TestOutHandler, msg string) {
   472  	t.Helper()
   473  	if output.Contains(msg) {
   474  		t.Errorf("Expected '%s' is not in the message, but got '%v'", msg, output.String())
   475  	}
   476  }
   477  
   478  // assert a cobra command is returning an error
   479  func assertCobraError(t *testing.T, app *runner.CmdSession, cmd string, expectedMessageContains string) {
   480  	t.Helper()
   481  	if err := runCobraCmd(app, cmd); err == nil {
   482  		t.Errorf("Expected error, but got none")
   483  	} else {
   484  		if !strings.Contains(err.Error(), expectedMessageContains) {
   485  			t.Errorf("Expected error message to contain '%s', but got '%s'", systools.PadString(expectedMessageContains, 80), err.Error())
   486  		}
   487  	}
   488  }
   489  
   490  // checking if we are in the expected path in the operting system
   491  func assertInOsPath(t *testing.T, path string) {
   492  	t.Helper()
   493  	if currentDir, err := os.Getwd(); err != nil {
   494  		t.Errorf("Expected no error, got '%v'", err)
   495  	} else {
   496  		if currentDir != path {
   497  			t.Errorf("Expected to be in '%v', got '%v'", path, currentDir)
   498  		}
   499  	}
   500  }
   501  
   502  var (
   503  	AcceptFullMatch          = 1
   504  	AcceptIgnoreLn           = 2
   505  	AcceptContains           = 3
   506  	AcceptContainsNoSpecials = 4
   507  )
   508  
   509  func assertStringFind(search, searchIn string, acceptableLevel int) bool {
   510  	if search == "" || searchIn == "" {
   511  		return true
   512  	}
   513  	if acceptableLevel >= AcceptFullMatch && searchIn == search {
   514  		return true
   515  	}
   516  	if acceptableLevel >= AcceptIgnoreLn && searchIn == search+"\n" {
   517  		return true
   518  	}
   519  	if acceptableLevel >= AcceptContains && strings.Contains(searchIn, search) {
   520  		return true
   521  	}
   522  
   523  	if acceptableLevel >= AcceptContainsNoSpecials {
   524  		search = strings.Replace(search, " ", "", -1)
   525  		searchIn = strings.Replace(searchIn, " ", "", -1)
   526  		search = strings.Replace(search, "\n", "", -1)
   527  		searchIn = strings.Replace(searchIn, "\n", "", -1)
   528  		search = strings.Replace(search, "\t", "", -1)
   529  		searchIn = strings.Replace(searchIn, "\t", "", -1)
   530  		if strings.Contains(searchIn, search) {
   531  			return true
   532  		}
   533  	}
   534  
   535  	return false
   536  }
   537  
   538  func assertStringFindInArray(search string, searchIn []string, acceptableLevel int) int {
   539  	for index, s := range searchIn {
   540  		if assertStringFind(search, s, acceptableLevel) {
   541  			return index
   542  		}
   543  	}
   544  	return -1
   545  }
   546  
   547  func assertFileExists(t *testing.T, file string) {
   548  	t.Helper()
   549  	file, err := filepath.Abs(file)
   550  	if err != nil {
   551  		t.Errorf("Error while trying to get the absolute path, got '%v'", err)
   552  	}
   553  	if _, err := os.Stat(file); os.IsNotExist(err) {
   554  		t.Errorf("Expected file '%s' exists, but got '%v'", file, err)
   555  	}
   556  }
   557  
   558  func assertFileContent(t *testing.T, file string, expectedContent string, acceptableLevel int) {
   559  	t.Helper()
   560  	if content, err := os.ReadFile(file); err != nil {
   561  		t.Errorf("Expected no error, got '%v'", err)
   562  	} else {
   563  		fileSlice := strings.Split(string(content), "\n")
   564  		expectedSlice := strings.Split(expectedContent, "\n")
   565  		// we want to check anything from the expectations is in the file
   566  		// but we need to make sure if we also have this in order
   567  		lastHit := -1
   568  		for _, expected := range expectedSlice {
   569  			hitAtIndex := assertStringFindInArray(expected, fileSlice, acceptableLevel)
   570  			if hitAtIndex == -1 {
   571  				t.Errorf("Expected file '%s' should contains '%s' what seems not be the case", file, expected)
   572  			}
   573  			if hitAtIndex < lastHit {
   574  				t.Errorf("Expected file '%s' contains '%s' but not in the right order", file, expected)
   575  			}
   576  			// remove the hit from the file slice, so we can check if we have duplicates.
   577  			// this is also nessary to check if we have the same line multiple times and do
   578  			// not fail because we found it on the wrong index
   579  			if hitAtIndex != -1 {
   580  				systools.RemoveFromSliceOnce(fileSlice, fileSlice[hitAtIndex])
   581  			}
   582  			lastHit = hitAtIndex
   583  		}
   584  	}
   585  }
   586  
   587  type find_flags int
   588  
   589  const (
   590  	FindFlagsNone       find_flags = iota
   591  	IgnoreTabs                     // ignore all tabs in the content and the message
   592  	IgnoreSpaces                   // ignore all spaces in the content and the message
   593  	IgnoreNewLines                 // ignore all new lines in the content and the message
   594  	IgnoreMultiSpaces              // ignore all repeated spaces and tabs in the content and the message
   595  	IgnoreCaseSensitive            // ignore case sensitive
   596  )
   597  
   598  // assert a string is part of the output buffer where we can ignore some flags
   599  // like tabs, spaces, new lines, case sensitive
   600  func assertInContent(t *testing.T, content string, msg string, flags ...find_flags) {
   601  	t.Helper()
   602  
   603  	if len(flags) > 0 {
   604  		for _, flag := range flags {
   605  			switch flag {
   606  			case IgnoreTabs:
   607  				content = strings.ReplaceAll(content, "\t", "")
   608  				msg = strings.ReplaceAll(msg, "\t", "")
   609  			case IgnoreSpaces:
   610  				content = strings.ReplaceAll(content, " ", "")
   611  				msg = strings.ReplaceAll(msg, " ", "")
   612  			case IgnoreNewLines:
   613  				content = strings.ReplaceAll(content, "\n", "")
   614  				msg = strings.ReplaceAll(msg, "\n", "")
   615  			case IgnoreMultiSpaces:
   616  				content = systools.TrimAllSpaces(content)
   617  				msg = systools.TrimAllSpaces(msg)
   618  			case IgnoreCaseSensitive:
   619  				content = strings.ToLower(content)
   620  				msg = strings.ToLower(msg)
   621  			}
   622  		}
   623  	}
   624  	if !strings.Contains(content, msg) {
   625  		t.Errorf("Expected to find '%s' in string. but did not found", msg)
   626  	}
   627  }
   628  
   629  func assertStringSliceInContent(t *testing.T, content string, msg []string, flags ...find_flags) {
   630  	t.Helper()
   631  	for _, line := range msg {
   632  		assertInContent(t, content, line, flags...)
   633  	}
   634  }