main_test.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /*
  2. * ECAL
  3. *
  4. * Copyright 2020 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package interpreter
  11. import (
  12. "flag"
  13. "fmt"
  14. "os"
  15. "sync"
  16. "testing"
  17. "devt.de/krotik/common/datautil"
  18. "devt.de/krotik/common/timeutil"
  19. "devt.de/krotik/ecal/engine"
  20. "devt.de/krotik/ecal/parser"
  21. "devt.de/krotik/ecal/scope"
  22. "devt.de/krotik/ecal/util"
  23. )
  24. // Main function for all tests in this package
  25. func TestMain(m *testing.M) {
  26. flag.Parse()
  27. // Run the tests
  28. res := m.Run()
  29. // Check if all nodes have been tested
  30. for n := range providerMap {
  31. if _, ok := usedNodes[n]; !ok {
  32. fmt.Println("Not tested node: ", n)
  33. }
  34. }
  35. os.Exit(res)
  36. }
  37. // Used nodes map which is filled during unit testing. Prefilled only with nodes
  38. // which should not be encountered in ASTs.
  39. //
  40. var usedNodes = map[string]bool{
  41. parser.NodeEOF: true,
  42. }
  43. var usedNodesLock = &sync.Mutex{}
  44. // Debuggger to be used
  45. //
  46. var testDebugger util.ECALDebugger
  47. // Last used logger
  48. //
  49. var testlogger *util.MemoryLogger
  50. // Last used cron
  51. //
  52. var testcron *timeutil.Cron
  53. // Last used processor
  54. //
  55. var testprocessor engine.Processor
  56. func UnitTestEval(input string, vs parser.Scope) (interface{}, error) {
  57. return UnitTestEvalAndAST(input, vs, "")
  58. }
  59. func UnitTestEvalAndAST(input string, vs parser.Scope, expectedAST string) (interface{}, error) {
  60. return UnitTestEvalAndASTAndImport(input, vs, expectedAST, nil)
  61. }
  62. func UnitTestEvalWithRuntimeProvider(input string, vs parser.Scope,
  63. erp *ECALRuntimeProvider) (interface{}, error) {
  64. return UnitTestEvalAndASTAndImportAndRuntimeProvider(input, vs, "", nil, erp)
  65. }
  66. func UnitTestEvalAndASTAndImport(input string, vs parser.Scope, expectedAST string,
  67. importLocator util.ECALImportLocator) (interface{}, error) {
  68. return UnitTestEvalAndASTAndImportAndRuntimeProvider(input, vs, expectedAST, importLocator, nil)
  69. }
  70. func UnitTestEvalAndASTAndImportAndRuntimeProvider(input string, vs parser.Scope, expectedAST string,
  71. importLocator util.ECALImportLocator, erp *ECALRuntimeProvider) (interface{}, error) {
  72. var traverseAST func(n *parser.ASTNode)
  73. traverseAST = func(n *parser.ASTNode) {
  74. if n.Name == "" {
  75. panic(fmt.Sprintf("Node found with empty string name: %s", n))
  76. }
  77. usedNodesLock.Lock()
  78. usedNodes[n.Name] = true
  79. usedNodesLock.Unlock()
  80. for _, cn := range n.Children {
  81. traverseAST(cn)
  82. }
  83. }
  84. // Parse the input
  85. if erp == nil {
  86. erp = NewECALRuntimeProvider("ECALTestRuntime", importLocator, nil)
  87. }
  88. // Set debugger
  89. erp.Debugger = testDebugger
  90. testlogger = erp.Logger.(*util.MemoryLogger)
  91. // For testing we change the cron object to be a testing cron which goes
  92. // quickly through a day when started. To test cron functionality a test
  93. // needs to first specify a setCronTrigger and the sinks. Once this has
  94. // been done the testcron object needs to be started. It will go through
  95. // a day instantly and add a deterministic number of events (according to
  96. // the cronspec given to setCronTrigger for one day).
  97. erp.Cron.Stop()
  98. erp.Cron = timeutil.NewTestingCronDay()
  99. testcron = erp.Cron
  100. testprocessor = erp.Processor
  101. ast, err := parser.ParseWithRuntime("ECALEvalTest", input, erp)
  102. if err != nil {
  103. return nil, err
  104. }
  105. traverseAST(ast)
  106. if expectedAST != "" && ast.String() != expectedAST {
  107. return nil, fmt.Errorf("Unexpected AST result:\n%v", ast.String())
  108. }
  109. // Validate input
  110. if err := ast.Runtime.Validate(); err != nil {
  111. return nil, err
  112. }
  113. if vs == nil {
  114. vs = scope.NewScope(scope.GlobalScope)
  115. }
  116. return ast.Runtime.Eval(vs, make(map[string]interface{}), erp.NewThreadID())
  117. }
  118. /*
  119. addLogFunction adds a simple log function to a given Scope.
  120. */
  121. func addLogFunction(vs parser.Scope) *datautil.RingBuffer {
  122. buf := datautil.NewRingBuffer(20)
  123. vs.SetValue("testlog", &TestLogger{buf})
  124. return buf
  125. }
  126. /*
  127. TestLogger is a simple logger function which can be added to tess.
  128. */
  129. type TestLogger struct {
  130. buf *datautil.RingBuffer
  131. }
  132. func (tl *TestLogger) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
  133. tl.buf.Add(fmt.Sprint(args...))
  134. return nil, nil
  135. }
  136. func (tl *TestLogger) DocString() (string, error) {
  137. return "testlogger docstring", nil
  138. }
  139. func (tl *TestLogger) String() string {
  140. return "TestLogger"
  141. }
  142. func (tl *TestLogger) MarshalJSON() ([]byte, error) {
  143. return []byte(tl.String()), nil
  144. }