github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/test_compare.go (about) 1 package lang 2 3 /* 4 This test library relates to the testing framework within the murex 5 language itself rather than Go's test framework within the murex project. 6 7 The naming convention here is basically the inverse of Go's test naming 8 convention. ie Go source files will be named "test_unit.go" (because 9 calling it unit_test.go would mean it's a Go test rather than murex test) 10 and the code is named UnitTestPlans (etc) rather than TestUnitPlans (etc) 11 because the latter might suggest they would be used by `go test`. This 12 naming convention is a little counterintuitive but it at least avoids 13 naming conflicts with `go test`. 14 */ 15 16 import ( 17 "github.com/lmorg/murex/utils" 18 ) 19 20 // Compare is the method which actually runs the individual test cases 21 // to see if they pass or fail. 22 func (tests *Tests) Compare(name string, p *Process) { 23 tests.mutex.Lock() 24 25 var i int 26 for ; i < len(tests.test); i++ { 27 if tests.test[i].Name == name { 28 goto compare 29 } 30 } 31 32 tests.mutex.Unlock() 33 34 tests.AddResult(&TestProperties{Name: name}, p, TestError, "Test named but there is no test defined") 35 return 36 37 compare: 38 39 var failed bool 40 test := tests.test[i] 41 test.HasRan = true 42 tests.mutex.Unlock() 43 44 // read stdout 45 stdout, err := test.out.stdio.ReadAll() 46 if err != nil { 47 failed = true 48 tests.AddResult(test, p, TestError, tMsgReadErr("stdout", name, err)) 49 } 50 stdout = utils.CrLfTrim(stdout) 51 52 // read stderr 53 stderr, err := test.err.stdio.ReadAll() 54 if err != nil { 55 failed = true 56 tests.AddResult(test, p, TestError, tMsgReadErr("stderr", name, err)) 57 } 58 stderr = utils.CrLfTrim(stderr) 59 60 // compare stdout 61 if len(test.out.Block) != 0 { 62 testBlock(test, p, test.out.Block, stdout, test.out.stdio.GetDataType(), "StdoutBlock", &failed) 63 } 64 65 if test.out.Regexp != nil { 66 if test.out.Regexp.Match(stdout) { 67 tests.AddResult(test, p, TestInfo, tMsgRegexMatch("StdoutRegex")) 68 } else { 69 failed = true 70 tests.AddResult(test, p, TestFailed, tMsgRegexMismatch("StdoutRegex", stdout)) 71 } 72 } 73 74 // compare stderr 75 if len(test.err.Block) != 0 { 76 testBlock(test, p, test.err.Block, stderr, test.err.stdio.GetDataType(), "StderrBlock", &failed) 77 } 78 79 if test.err.Regexp != nil { 80 if test.err.Regexp.Match(stderr) { 81 tests.AddResult(test, p, TestInfo, tMsgRegexMatch("StderrRegex")) 82 } else { 83 failed = true 84 tests.AddResult(test, p, TestFailed, tMsgRegexMismatch("StderrRegex", stderr)) 85 } 86 } 87 88 // test exit number 89 if test.exitNum != *test.exitNumPtr { 90 failed = true 91 tests.AddResult(test, p, TestFailed, tMsgExitNumMismatch(test.exitNum, *test.exitNumPtr)) 92 93 } else { 94 tests.AddResult(test, p, TestInfo, tMsgExitNumMatch()) 95 } 96 97 // if not failed, log a success result 98 if !failed { 99 tests.AddResult(test, p, TestPassed, tMsgPassed()) 100 } 101 } 102 103 // ReportMissedTests is used so we have a result of tests that didn't run 104 func (tests *Tests) ReportMissedTests(p *Process) { 105 tests.mutex.Lock() 106 107 for _, test := range tests.test { 108 if test.HasRan { 109 continue 110 } 111 112 tests.AddResult(test, &Process{Config: p.Config}, TestMissed, "Test was defined but no function ran against that test pipe.") 113 } 114 115 tests.mutex.Unlock() 116 } 117 118 func testIsMap(b []byte, dt string, property string) (TestStatus, string) { 119 fork := ShellProcess.Fork(F_CREATE_STDIN) 120 fork.Stdin.SetDataType(dt) 121 _, err := fork.Stdin.Write(b) 122 if err != nil { 123 return TestError, tMsgWriteErr(property, err) 124 } 125 126 v, err := UnmarshalData(fork.Process, dt) 127 if err != nil { 128 return TestFailed, tMsgUnmarshalErr(property, dt, err) 129 } 130 131 switch v.(type) { 132 case map[string]string, map[string]interface{}, map[interface{}]string, map[interface{}]interface{}: 133 return TestPassed, tMsgDataFormatValid(property, dt, v) 134 135 default: 136 return TestFailed, tMsgDataFormatInvalid(property, dt, v) 137 } 138 } 139 140 func testIsArray(b []byte, dt string, property string) (TestStatus, string) { 141 fork := ShellProcess.Fork(F_CREATE_STDIN) 142 fork.Stdin.SetDataType(dt) 143 _, err := fork.Stdin.Write(b) 144 if err != nil { 145 return TestError, tMsgWriteErr(property, err) 146 } 147 148 v, err := UnmarshalData(fork.Process, dt) 149 if err != nil { 150 return TestFailed, tMsgUnmarshalErr(property, dt, err) 151 } 152 153 switch v.(type) { 154 case []string, []interface{}: 155 return TestPassed, tMsgDataFormatValid(property, dt, v) 156 157 default: 158 return TestFailed, tMsgDataFormatInvalid(property, dt, v) 159 } 160 } 161 162 func testIsGreaterThanOrEqualTo(b []byte, dt string, property string, comparison int) (TestStatus, string) { 163 fork := ShellProcess.Fork(F_CREATE_STDIN) 164 fork.Stdin.SetDataType(dt) 165 _, err := fork.Stdin.Write(b) 166 if err != nil { 167 return TestError, tMsgWriteErr(property, err) 168 } 169 170 v, err := UnmarshalData(fork.Process, dt) 171 if err != nil { 172 return TestFailed, tMsgUnmarshalErr(property, dt, err) 173 } 174 175 var l int 176 switch t := v.(type) { 177 case []string: 178 l = len(t) 179 case []float64: 180 l = len(t) 181 case []int: 182 l = len(t) 183 case []bool: 184 l = len(t) 185 case []any: 186 l = len(t) 187 188 case [][]string: 189 l = len(t) 190 case [][]any: 191 l = len(t) 192 193 case map[string]string: 194 l = len(t) 195 case map[string]any: 196 l = len(t) 197 case map[any]any: 198 l = len(t) 199 200 default: 201 return TestFailed, tMsgDataFormatInvalid(property, dt, v) 202 } 203 204 if l >= comparison { 205 return TestPassed, tMsgGtEqMatch(property, l, comparison) 206 } 207 return TestFailed, tMsgGtEqFail(property, l, comparison) 208 }