evaluation_test.go 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516
  1. package govaluate
  2. import (
  3. "errors"
  4. "fmt"
  5. "regexp"
  6. "testing"
  7. "time"
  8. )
  9. /*
  10. Represents a test of expression evaluation
  11. */
  12. type EvaluationTest struct {
  13. Name string
  14. Input string
  15. Functions map[string]ExpressionFunction
  16. Parameters []EvaluationParameter
  17. Expected interface{}
  18. }
  19. type EvaluationParameter struct {
  20. Name string
  21. Value interface{}
  22. }
  23. func TestNoParameterEvaluation(test *testing.T) {
  24. evaluationTests := []EvaluationTest{
  25. EvaluationTest{
  26. Name: "Single PLUS",
  27. Input: "51 + 49",
  28. Expected: 100.0,
  29. },
  30. EvaluationTest{
  31. Name: "Single MINUS",
  32. Input: "100 - 51",
  33. Expected: 49.0,
  34. },
  35. EvaluationTest{
  36. Name: "Single BITWISE AND",
  37. Input: "100 & 50",
  38. Expected: 32.0,
  39. },
  40. EvaluationTest{
  41. Name: "Single BITWISE OR",
  42. Input: "100 | 50",
  43. Expected: 118.0,
  44. },
  45. EvaluationTest{
  46. Name: "Single BITWISE XOR",
  47. Input: "100 ^ 50",
  48. Expected: 86.0,
  49. },
  50. EvaluationTest{
  51. Name: "Single shift left",
  52. Input: "2 << 1",
  53. Expected: 4.0,
  54. },
  55. EvaluationTest{
  56. Name: "Single shift right",
  57. Input: "2 >> 1",
  58. Expected: 1.0,
  59. },
  60. EvaluationTest{
  61. Name: "Single BITWISE NOT",
  62. Input: "~10",
  63. Expected: -11.0,
  64. },
  65. EvaluationTest{
  66. Name: "Single MULTIPLY",
  67. Input: "5 * 20",
  68. Expected: 100.0,
  69. },
  70. EvaluationTest{
  71. Name: "Single DIVIDE",
  72. Input: "100 / 20",
  73. Expected: 5.0,
  74. },
  75. EvaluationTest{
  76. Name: "Single even MODULUS",
  77. Input: "100 % 2",
  78. Expected: 0.0,
  79. },
  80. EvaluationTest{
  81. Name: "Single odd MODULUS",
  82. Input: "101 % 2",
  83. Expected: 1.0,
  84. },
  85. EvaluationTest{
  86. Name: "Single EXPONENT",
  87. Input: "10 ** 2",
  88. Expected: 100.0,
  89. },
  90. EvaluationTest{
  91. Name: "Compound PLUS",
  92. Input: "20 + 30 + 50",
  93. Expected: 100.0,
  94. },
  95. EvaluationTest{
  96. Name: "Compound BITWISE AND",
  97. Input: "20 & 30 & 50",
  98. Expected: 16.0,
  99. },
  100. EvaluationTest{
  101. Name: "Mutiple operators",
  102. Input: "20 * 5 - 49",
  103. Expected: 51.0,
  104. },
  105. EvaluationTest{
  106. Name: "Parenthesis usage",
  107. Input: "100 - (5 * 10)",
  108. Expected: 50.0,
  109. },
  110. EvaluationTest{
  111. Name: "Nested parentheses",
  112. Input: "50 + (5 * (15 - 5))",
  113. Expected: 100.0,
  114. },
  115. EvaluationTest{
  116. Name: "Nested parentheses with bitwise",
  117. Input: "100 ^ (23 * (2 | 5))",
  118. Expected: 197.0,
  119. },
  120. EvaluationTest{
  121. Name: "Logical OR operation of two clauses",
  122. Input: "(1 == 1) || (true == true)",
  123. Expected: true,
  124. },
  125. EvaluationTest{
  126. Name: "Logical AND operation of two clauses",
  127. Input: "(1 == 1) && (true == true)",
  128. Expected: true,
  129. },
  130. EvaluationTest{
  131. Name: "Implicit boolean",
  132. Input: "2 > 1",
  133. Expected: true,
  134. },
  135. EvaluationTest{
  136. Name: "Compound boolean",
  137. Input: "5 < 10 && 1 < 5",
  138. Expected: true,
  139. },
  140. EvaluationTest{
  141. Name: "Evaluated true && false operation (for issue #8)",
  142. Input: "1 > 10 && 11 > 10",
  143. Expected: false,
  144. },
  145. EvaluationTest{
  146. Name: "Evaluated true && false operation (for issue #8)",
  147. Input: "true == true && false == true",
  148. Expected: false,
  149. },
  150. EvaluationTest{
  151. Name: "Parenthesis boolean",
  152. Input: "10 < 50 && (1 != 2 && 1 > 0)",
  153. Expected: true,
  154. },
  155. EvaluationTest{
  156. Name: "Comparison of string constants",
  157. Input: "'foo' == 'foo'",
  158. Expected: true,
  159. },
  160. EvaluationTest{
  161. Name: "NEQ comparison of string constants",
  162. Input: "'foo' != 'bar'",
  163. Expected: true,
  164. },
  165. EvaluationTest{
  166. Name: "REQ comparison of string constants",
  167. Input: "'foobar' =~ 'oba'",
  168. Expected: true,
  169. },
  170. EvaluationTest{
  171. Name: "NREQ comparison of string constants",
  172. Input: "'foo' !~ 'bar'",
  173. Expected: true,
  174. },
  175. EvaluationTest{
  176. Name: "Multiplicative/additive order",
  177. Input: "5 + 10 * 2",
  178. Expected: 25.0,
  179. },
  180. EvaluationTest{
  181. Name: "Multiple constant multiplications",
  182. Input: "10 * 10 * 10",
  183. Expected: 1000.0,
  184. },
  185. EvaluationTest{
  186. Name: "Multiple adds/multiplications",
  187. Input: "10 * 10 * 10 + 1 * 10 * 10",
  188. Expected: 1100.0,
  189. },
  190. EvaluationTest{
  191. Name: "Modulus precedence",
  192. Input: "1 + 101 % 2 * 5",
  193. Expected: 6.0,
  194. },
  195. EvaluationTest{
  196. Name: "Exponent precedence",
  197. Input: "1 + 5 ** 3 % 2 * 5",
  198. Expected: 6.0,
  199. },
  200. EvaluationTest{
  201. Name: "Bit shift precedence",
  202. Input: "50 << 1 & 90",
  203. Expected: 64.0,
  204. },
  205. EvaluationTest{
  206. Name: "Bit shift precedence",
  207. Input: "90 & 50 << 1",
  208. Expected: 64.0,
  209. },
  210. EvaluationTest{
  211. Name: "Bit shift precedence amongst non-bitwise",
  212. Input: "90 + 50 << 1 * 5",
  213. Expected: 4480.0,
  214. },
  215. EvaluationTest{
  216. Name: "Order of non-commutative same-precedence operators (additive)",
  217. Input: "1 - 2 - 4 - 8",
  218. Expected: -13.0,
  219. },
  220. EvaluationTest{
  221. Name: "Order of non-commutative same-precedence operators (multiplicative)",
  222. Input: "1 * 4 / 2 * 8",
  223. Expected: 16.0,
  224. },
  225. EvaluationTest{
  226. Name: "Null coalesce precedence",
  227. Input: "true ?? true ? 100 + 200 : 400",
  228. Expected: 300.0,
  229. },
  230. EvaluationTest{
  231. Name: "Identical date equivalence",
  232. Input: "'2014-01-02 14:12:22' == '2014-01-02 14:12:22'",
  233. Expected: true,
  234. },
  235. EvaluationTest{
  236. Name: "Positive date GT",
  237. Input: "'2014-01-02 14:12:22' > '2014-01-02 12:12:22'",
  238. Expected: true,
  239. },
  240. EvaluationTest{
  241. Name: "Negative date GT",
  242. Input: "'2014-01-02 14:12:22' > '2014-01-02 16:12:22'",
  243. Expected: false,
  244. },
  245. EvaluationTest{
  246. Name: "Positive date GTE",
  247. Input: "'2014-01-02 14:12:22' >= '2014-01-02 12:12:22'",
  248. Expected: true,
  249. },
  250. EvaluationTest{
  251. Name: "Negative date GTE",
  252. Input: "'2014-01-02 14:12:22' >= '2014-01-02 16:12:22'",
  253. Expected: false,
  254. },
  255. EvaluationTest{
  256. Name: "Positive date LT",
  257. Input: "'2014-01-02 14:12:22' < '2014-01-02 16:12:22'",
  258. Expected: true,
  259. },
  260. EvaluationTest{
  261. Name: "Negative date LT",
  262. Input: "'2014-01-02 14:12:22' < '2014-01-02 11:12:22'",
  263. Expected: false,
  264. },
  265. EvaluationTest{
  266. Name: "Positive date LTE",
  267. Input: "'2014-01-02 09:12:22' <= '2014-01-02 12:12:22'",
  268. Expected: true,
  269. },
  270. EvaluationTest{
  271. Name: "Negative date LTE",
  272. Input: "'2014-01-02 14:12:22' <= '2014-01-02 11:12:22'",
  273. Expected: false,
  274. },
  275. EvaluationTest{
  276. Name: "Sign prefix comparison",
  277. Input: "-1 < 0",
  278. Expected: true,
  279. },
  280. EvaluationTest{
  281. Name: "Lexicographic LT",
  282. Input: "'ab' < 'abc'",
  283. Expected: true,
  284. },
  285. EvaluationTest{
  286. Name: "Lexicographic LTE",
  287. Input: "'ab' <= 'abc'",
  288. Expected: true,
  289. },
  290. EvaluationTest{
  291. Name: "Lexicographic GT",
  292. Input: "'aba' > 'abc'",
  293. Expected: false,
  294. },
  295. EvaluationTest{
  296. Name: "Lexicographic GTE",
  297. Input: "'aba' >= 'abc'",
  298. Expected: false,
  299. },
  300. EvaluationTest{
  301. Name: "Boolean sign prefix comparison",
  302. Input: "!true == false",
  303. Expected: true,
  304. },
  305. EvaluationTest{
  306. Name: "Inversion of clause",
  307. Input: "!(10 < 0)",
  308. Expected: true,
  309. },
  310. EvaluationTest{
  311. Name: "Negation after modifier",
  312. Input: "10 * -10",
  313. Expected: -100.0,
  314. },
  315. EvaluationTest{
  316. Name: "Ternary with single boolean",
  317. Input: "true ? 10",
  318. Expected: 10.0,
  319. },
  320. EvaluationTest{
  321. Name: "Ternary nil with single boolean",
  322. Input: "false ? 10",
  323. Expected: nil,
  324. },
  325. EvaluationTest{
  326. Name: "Ternary with comparator boolean",
  327. Input: "10 > 5 ? 35.50",
  328. Expected: 35.50,
  329. },
  330. EvaluationTest{
  331. Name: "Ternary nil with comparator boolean",
  332. Input: "1 > 5 ? 35.50",
  333. Expected: nil,
  334. },
  335. EvaluationTest{
  336. Name: "Ternary with parentheses",
  337. Input: "(5 * (15 - 5)) > 5 ? 35.50",
  338. Expected: 35.50,
  339. },
  340. EvaluationTest{
  341. Name: "Ternary precedence",
  342. Input: "true ? 35.50 > 10",
  343. Expected: true,
  344. },
  345. EvaluationTest{
  346. Name: "Ternary-else",
  347. Input: "false ? 35.50 : 50",
  348. Expected: 50.0,
  349. },
  350. EvaluationTest{
  351. Name: "Ternary-else inside clause",
  352. Input: "(false ? 5 : 35.50) > 10",
  353. Expected: true,
  354. },
  355. EvaluationTest{
  356. Name: "Ternary-else (true-case) inside clause",
  357. Input: "(true ? 1 : 5) < 10",
  358. Expected: true,
  359. },
  360. EvaluationTest{
  361. Name: "Ternary-else before comparator (negative case)",
  362. Input: "true ? 1 : 5 > 10",
  363. Expected: 1.0,
  364. },
  365. EvaluationTest{
  366. Name: "Nested ternaries (#32)",
  367. Input: "(2 == 2) ? 1 : (true ? 2 : 3)",
  368. Expected: 1.0,
  369. },
  370. EvaluationTest{
  371. Name: "Nested ternaries, right case (#32)",
  372. Input: "false ? 1 : (true ? 2 : 3)",
  373. Expected: 2.0,
  374. },
  375. EvaluationTest{
  376. Name: "Doubly-nested ternaries (#32)",
  377. Input: "true ? (false ? 1 : (false ? 2 : 3)) : (false ? 4 : 5)",
  378. Expected: 3.0,
  379. },
  380. EvaluationTest{
  381. Name: "String to string concat",
  382. Input: "'foo' + 'bar' == 'foobar'",
  383. Expected: true,
  384. },
  385. EvaluationTest{
  386. Name: "String to float64 concat",
  387. Input: "'foo' + 123 == 'foo123'",
  388. Expected: true,
  389. },
  390. EvaluationTest{
  391. Name: "Float64 to string concat",
  392. Input: "123 + 'bar' == '123bar'",
  393. Expected: true,
  394. },
  395. EvaluationTest{
  396. Name: "String to date concat",
  397. Input: "'foo' + '02/05/1970' == 'foobar'",
  398. Expected: false,
  399. },
  400. EvaluationTest{
  401. Name: "String to bool concat",
  402. Input: "'foo' + true == 'footrue'",
  403. Expected: true,
  404. },
  405. EvaluationTest{
  406. Name: "Bool to string concat",
  407. Input: "true + 'bar' == 'truebar'",
  408. Expected: true,
  409. },
  410. EvaluationTest{
  411. Name: "Null coalesce left",
  412. Input: "1 ?? 2",
  413. Expected: 1.0,
  414. },
  415. EvaluationTest{
  416. Name: "Array membership literals",
  417. Input: "1 in (1, 2, 3)",
  418. Expected: true,
  419. },
  420. EvaluationTest{
  421. Name: "Array membership literal with inversion",
  422. Input: "!(1 in (1, 2, 3))",
  423. Expected: false,
  424. },
  425. EvaluationTest{
  426. Name: "Logical operator reordering (#30)",
  427. Input: "(true && true) || (true && false)",
  428. Expected: true,
  429. },
  430. EvaluationTest{
  431. Name: "Logical operator reordering without parens (#30)",
  432. Input: "true && true || true && false",
  433. Expected: true,
  434. },
  435. EvaluationTest{
  436. Name: "Logical operator reordering with multiple OR (#30)",
  437. Input: "false || true && true || false",
  438. Expected: true,
  439. },
  440. EvaluationTest{
  441. Name: "Left-side multiple consecutive (should be reordered) operators",
  442. Input: "(10 * 10 * 10) > 10",
  443. Expected: true,
  444. },
  445. EvaluationTest{
  446. Name: "Three-part non-paren logical op reordering (#44)",
  447. Input: "false && true || true",
  448. Expected: true,
  449. },
  450. EvaluationTest{
  451. Name: "Three-part non-paren logical op reordering (#44), second one",
  452. Input: "true || false && true",
  453. Expected: true,
  454. },
  455. EvaluationTest{
  456. Name: "Logical operator reordering without parens (#45)",
  457. Input: "true && true || false && false",
  458. Expected: true,
  459. },
  460. EvaluationTest{
  461. Name: "Single function",
  462. Input: "foo()",
  463. Functions: map[string]ExpressionFunction{
  464. "foo": func(arguments ...interface{}) (interface{}, error) {
  465. return true, nil
  466. },
  467. },
  468. Expected: true,
  469. },
  470. EvaluationTest{
  471. Name: "Function with argument",
  472. Input: "passthrough(1)",
  473. Functions: map[string]ExpressionFunction{
  474. "passthrough": func(arguments ...interface{}) (interface{}, error) {
  475. return arguments[0], nil
  476. },
  477. },
  478. Expected: 1.0,
  479. },
  480. EvaluationTest{
  481. Name: "Function with arguments",
  482. Input: "passthrough(1, 2)",
  483. Functions: map[string]ExpressionFunction{
  484. "passthrough": func(arguments ...interface{}) (interface{}, error) {
  485. return arguments[0].(float64) + arguments[1].(float64), nil
  486. },
  487. },
  488. Expected: 3.0,
  489. },
  490. EvaluationTest{
  491. Name: "Nested function with precedence",
  492. Input: "sum(1, sum(2, 3), 2 + 2, true ? 4 : 5)",
  493. Functions: map[string]ExpressionFunction{
  494. "sum": func(arguments ...interface{}) (interface{}, error) {
  495. sum := 0.0
  496. for _, v := range arguments {
  497. sum += v.(float64)
  498. }
  499. return sum, nil
  500. },
  501. },
  502. Expected: 14.0,
  503. },
  504. EvaluationTest{
  505. Name: "Empty function and modifier, compared",
  506. Input: "numeric()-1 > 0",
  507. Functions: map[string]ExpressionFunction{
  508. "numeric": func(arguments ...interface{}) (interface{}, error) {
  509. return 2.0, nil
  510. },
  511. },
  512. Expected: true,
  513. },
  514. EvaluationTest{
  515. Name: "Empty function comparator",
  516. Input: "numeric() > 0",
  517. Functions: map[string]ExpressionFunction{
  518. "numeric": func(arguments ...interface{}) (interface{}, error) {
  519. return 2.0, nil
  520. },
  521. },
  522. Expected: true,
  523. },
  524. EvaluationTest{
  525. Name: "Empty function logical operator",
  526. Input: "success() && !false",
  527. Functions: map[string]ExpressionFunction{
  528. "success": func(arguments ...interface{}) (interface{}, error) {
  529. return true, nil
  530. },
  531. },
  532. Expected: true,
  533. },
  534. EvaluationTest{
  535. Name: "Empty function ternary",
  536. Input: "nope() ? 1 : 2.0",
  537. Functions: map[string]ExpressionFunction{
  538. "nope": func(arguments ...interface{}) (interface{}, error) {
  539. return false, nil
  540. },
  541. },
  542. Expected: 2.0,
  543. },
  544. EvaluationTest{
  545. Name: "Empty function null coalesce",
  546. Input: "null() ?? 2",
  547. Functions: map[string]ExpressionFunction{
  548. "null": func(arguments ...interface{}) (interface{}, error) {
  549. return nil, nil
  550. },
  551. },
  552. Expected: 2.0,
  553. },
  554. EvaluationTest{
  555. Name: "Empty function with prefix",
  556. Input: "-ten()",
  557. Functions: map[string]ExpressionFunction{
  558. "ten": func(arguments ...interface{}) (interface{}, error) {
  559. return 10.0, nil
  560. },
  561. },
  562. Expected: -10.0,
  563. },
  564. EvaluationTest{
  565. Name: "Empty function as part of chain",
  566. Input: "10 - numeric() - 2",
  567. Functions: map[string]ExpressionFunction{
  568. "numeric": func(arguments ...interface{}) (interface{}, error) {
  569. return 5.0, nil
  570. },
  571. },
  572. Expected: 3.0,
  573. },
  574. EvaluationTest{
  575. Name: "Empty function near separator",
  576. Input: "10 in (1, 2, 3, ten(), 8)",
  577. Functions: map[string]ExpressionFunction{
  578. "ten": func(arguments ...interface{}) (interface{}, error) {
  579. return 10.0, nil
  580. },
  581. },
  582. Expected: true,
  583. },
  584. EvaluationTest{
  585. Name: "Enclosed empty function with modifier and comparator (#28)",
  586. Input: "(ten() - 1) > 3",
  587. Functions: map[string]ExpressionFunction{
  588. "ten": func(arguments ...interface{}) (interface{}, error) {
  589. return 10.0, nil
  590. },
  591. },
  592. Expected: true,
  593. },
  594. EvaluationTest{
  595. Name: "Ternary/Java EL ambiguity",
  596. Input: "false ? foo:length()",
  597. Functions: map[string]ExpressionFunction{
  598. "length": func(arguments ...interface{}) (interface{}, error) {
  599. return 1.0, nil
  600. },
  601. },
  602. Expected: 1.0,
  603. },
  604. }
  605. runEvaluationTests(evaluationTests, test)
  606. }
  607. func TestParameterizedEvaluation(test *testing.T) {
  608. evaluationTests := []EvaluationTest{
  609. EvaluationTest{
  610. Name: "Single parameter modified by constant",
  611. Input: "foo + 2",
  612. Parameters: []EvaluationParameter{
  613. EvaluationParameter{
  614. Name: "foo",
  615. Value: 2.0,
  616. },
  617. },
  618. Expected: 4.0,
  619. },
  620. EvaluationTest{
  621. Name: "Single parameter modified by variable",
  622. Input: "foo * bar",
  623. Parameters: []EvaluationParameter{
  624. EvaluationParameter{
  625. Name: "foo",
  626. Value: 5.0,
  627. },
  628. EvaluationParameter{
  629. Name: "bar",
  630. Value: 2.0,
  631. },
  632. },
  633. Expected: 10.0,
  634. },
  635. EvaluationTest{
  636. Name: "Multiple multiplications of the same parameter",
  637. Input: "foo * foo * foo",
  638. Parameters: []EvaluationParameter{
  639. EvaluationParameter{
  640. Name: "foo",
  641. Value: 10.0,
  642. },
  643. },
  644. Expected: 1000.0,
  645. },
  646. EvaluationTest{
  647. Name: "Multiple additions of the same parameter",
  648. Input: "foo + foo + foo",
  649. Parameters: []EvaluationParameter{
  650. EvaluationParameter{
  651. Name: "foo",
  652. Value: 10.0,
  653. },
  654. },
  655. Expected: 30.0,
  656. },
  657. EvaluationTest{
  658. Name: "Parameter name sensitivity",
  659. Input: "foo + FoO + FOO",
  660. Parameters: []EvaluationParameter{
  661. EvaluationParameter{
  662. Name: "foo",
  663. Value: 8.0,
  664. },
  665. EvaluationParameter{
  666. Name: "FoO",
  667. Value: 4.0,
  668. },
  669. EvaluationParameter{
  670. Name: "FOO",
  671. Value: 2.0,
  672. },
  673. },
  674. Expected: 14.0,
  675. },
  676. EvaluationTest{
  677. Name: "Sign prefix comparison against prefixed variable",
  678. Input: "-1 < -foo",
  679. Parameters: []EvaluationParameter{
  680. EvaluationParameter{
  681. Name: "foo",
  682. Value: -8.0,
  683. },
  684. },
  685. Expected: true,
  686. },
  687. EvaluationTest{
  688. Name: "Fixed-point parameter",
  689. Input: "foo > 1",
  690. Parameters: []EvaluationParameter{
  691. EvaluationParameter{
  692. Name: "foo",
  693. Value: 2,
  694. },
  695. },
  696. Expected: true,
  697. },
  698. EvaluationTest{
  699. Name: "Modifier after closing clause",
  700. Input: "(2 + 2) + 2 == 6",
  701. Expected: true,
  702. },
  703. EvaluationTest{
  704. Name: "Comparator after closing clause",
  705. Input: "(2 + 2) >= 4",
  706. Expected: true,
  707. },
  708. EvaluationTest{
  709. Name: "Two-boolean logical operation (for issue #8)",
  710. Input: "(foo == true) || (bar == true)",
  711. Parameters: []EvaluationParameter{
  712. EvaluationParameter{
  713. Name: "foo",
  714. Value: true,
  715. },
  716. EvaluationParameter{
  717. Name: "bar",
  718. Value: false,
  719. },
  720. },
  721. Expected: true,
  722. },
  723. EvaluationTest{
  724. Name: "Two-variable integer logical operation (for issue #8)",
  725. Input: "foo > 10 && bar > 10",
  726. Parameters: []EvaluationParameter{
  727. EvaluationParameter{
  728. Name: "foo",
  729. Value: 1,
  730. },
  731. EvaluationParameter{
  732. Name: "bar",
  733. Value: 11,
  734. },
  735. },
  736. Expected: false,
  737. },
  738. EvaluationTest{
  739. Name: "Regex against right-hand parameter",
  740. Input: "'foobar' =~ foo",
  741. Parameters: []EvaluationParameter{
  742. EvaluationParameter{
  743. Name: "foo",
  744. Value: "obar",
  745. },
  746. },
  747. Expected: true,
  748. },
  749. EvaluationTest{
  750. Name: "Not-regex against right-hand parameter",
  751. Input: "'foobar' !~ foo",
  752. Parameters: []EvaluationParameter{
  753. EvaluationParameter{
  754. Name: "foo",
  755. Value: "baz",
  756. },
  757. },
  758. Expected: true,
  759. },
  760. EvaluationTest{
  761. Name: "Regex against two parameters",
  762. Input: "foo =~ bar",
  763. Parameters: []EvaluationParameter{
  764. EvaluationParameter{
  765. Name: "foo",
  766. Value: "foobar",
  767. },
  768. EvaluationParameter{
  769. Name: "bar",
  770. Value: "oba",
  771. },
  772. },
  773. Expected: true,
  774. },
  775. EvaluationTest{
  776. Name: "Not-regex against two parameters",
  777. Input: "foo !~ bar",
  778. Parameters: []EvaluationParameter{
  779. EvaluationParameter{
  780. Name: "foo",
  781. Value: "foobar",
  782. },
  783. EvaluationParameter{
  784. Name: "bar",
  785. Value: "baz",
  786. },
  787. },
  788. Expected: true,
  789. },
  790. EvaluationTest{
  791. Name: "Pre-compiled regex",
  792. Input: "foo =~ bar",
  793. Parameters: []EvaluationParameter{
  794. EvaluationParameter{
  795. Name: "foo",
  796. Value: "foobar",
  797. },
  798. EvaluationParameter{
  799. Name: "bar",
  800. Value: regexp.MustCompile("[fF][oO]+"),
  801. },
  802. },
  803. Expected: true,
  804. },
  805. EvaluationTest{
  806. Name: "Pre-compiled not-regex",
  807. Input: "foo !~ bar",
  808. Parameters: []EvaluationParameter{
  809. EvaluationParameter{
  810. Name: "foo",
  811. Value: "foobar",
  812. },
  813. EvaluationParameter{
  814. Name: "bar",
  815. Value: regexp.MustCompile("[fF][oO]+"),
  816. },
  817. },
  818. Expected: false,
  819. },
  820. EvaluationTest{
  821. Name: "Single boolean parameter",
  822. Input: "commission ? 10",
  823. Parameters: []EvaluationParameter{
  824. EvaluationParameter{
  825. Name: "commission",
  826. Value: true,
  827. },
  828. },
  829. Expected: 10.0,
  830. },
  831. EvaluationTest{
  832. Name: "True comparator with a parameter",
  833. Input: "partner == 'amazon' ? 10",
  834. Parameters: []EvaluationParameter{
  835. EvaluationParameter{
  836. Name: "partner",
  837. Value: "amazon",
  838. },
  839. },
  840. Expected: 10.0,
  841. },
  842. EvaluationTest{
  843. Name: "False comparator with a parameter",
  844. Input: "partner == 'amazon' ? 10",
  845. Parameters: []EvaluationParameter{
  846. EvaluationParameter{
  847. Name: "partner",
  848. Value: "ebay",
  849. },
  850. },
  851. Expected: nil,
  852. },
  853. EvaluationTest{
  854. Name: "True comparator with multiple parameters",
  855. Input: "theft && period == 24 ? 60",
  856. Parameters: []EvaluationParameter{
  857. EvaluationParameter{
  858. Name: "theft",
  859. Value: true,
  860. },
  861. EvaluationParameter{
  862. Name: "period",
  863. Value: 24,
  864. },
  865. },
  866. Expected: 60.0,
  867. },
  868. EvaluationTest{
  869. Name: "False comparator with multiple parameters",
  870. Input: "theft && period == 24 ? 60",
  871. Parameters: []EvaluationParameter{
  872. EvaluationParameter{
  873. Name: "theft",
  874. Value: false,
  875. },
  876. EvaluationParameter{
  877. Name: "period",
  878. Value: 24,
  879. },
  880. },
  881. Expected: nil,
  882. },
  883. EvaluationTest{
  884. Name: "String concat with single string parameter",
  885. Input: "foo + 'bar'",
  886. Parameters: []EvaluationParameter{
  887. EvaluationParameter{
  888. Name: "foo",
  889. Value: "baz",
  890. },
  891. },
  892. Expected: "bazbar",
  893. },
  894. EvaluationTest{
  895. Name: "String concat with multiple string parameter",
  896. Input: "foo + bar",
  897. Parameters: []EvaluationParameter{
  898. EvaluationParameter{
  899. Name: "foo",
  900. Value: "baz",
  901. },
  902. EvaluationParameter{
  903. Name: "bar",
  904. Value: "quux",
  905. },
  906. },
  907. Expected: "bazquux",
  908. },
  909. EvaluationTest{
  910. Name: "String concat with float parameter",
  911. Input: "foo + bar",
  912. Parameters: []EvaluationParameter{
  913. EvaluationParameter{
  914. Name: "foo",
  915. Value: "baz",
  916. },
  917. EvaluationParameter{
  918. Name: "bar",
  919. Value: 123.0,
  920. },
  921. },
  922. Expected: "baz123",
  923. },
  924. EvaluationTest{
  925. Name: "Mixed multiple string concat",
  926. Input: "foo + 123 + 'bar' + true",
  927. Parameters: []EvaluationParameter{
  928. EvaluationParameter{
  929. Name: "foo",
  930. Value: "baz",
  931. },
  932. },
  933. Expected: "baz123bartrue",
  934. },
  935. EvaluationTest{
  936. Name: "Integer width spectrum",
  937. Input: "uint8 + uint16 + uint32 + uint64 + int8 + int16 + int32 + int64",
  938. Parameters: []EvaluationParameter{
  939. EvaluationParameter{
  940. Name: "uint8",
  941. Value: uint8(0),
  942. },
  943. EvaluationParameter{
  944. Name: "uint16",
  945. Value: uint16(0),
  946. },
  947. EvaluationParameter{
  948. Name: "uint32",
  949. Value: uint32(0),
  950. },
  951. EvaluationParameter{
  952. Name: "uint64",
  953. Value: uint64(0),
  954. },
  955. EvaluationParameter{
  956. Name: "int8",
  957. Value: int8(0),
  958. },
  959. EvaluationParameter{
  960. Name: "int16",
  961. Value: int16(0),
  962. },
  963. EvaluationParameter{
  964. Name: "int32",
  965. Value: int32(0),
  966. },
  967. EvaluationParameter{
  968. Name: "int64",
  969. Value: int64(0),
  970. },
  971. },
  972. Expected: 0.0,
  973. },
  974. EvaluationTest{
  975. Name: "Floats",
  976. Input: "float32 + float64",
  977. Parameters: []EvaluationParameter{
  978. EvaluationParameter{
  979. Name: "float32",
  980. Value: float32(0.0),
  981. },
  982. EvaluationParameter{
  983. Name: "float64",
  984. Value: float64(0.0),
  985. },
  986. },
  987. Expected: 0.0,
  988. },
  989. EvaluationTest{
  990. Name: "Null coalesce right",
  991. Input: "foo ?? 1.0",
  992. Parameters: []EvaluationParameter{
  993. EvaluationParameter{
  994. Name: "foo",
  995. Value: nil,
  996. },
  997. },
  998. Expected: 1.0,
  999. },
  1000. EvaluationTest{
  1001. Name: "Multiple comparator/logical operators (#30)",
  1002. Input: "(foo >= 2887057408 && foo <= 2887122943) || (foo >= 168100864 && foo <= 168118271)",
  1003. Parameters: []EvaluationParameter{
  1004. EvaluationParameter{
  1005. Name: "foo",
  1006. Value: 2887057409,
  1007. },
  1008. },
  1009. Expected: true,
  1010. },
  1011. EvaluationTest{
  1012. Name: "Multiple comparator/logical operators, opposite order (#30)",
  1013. Input: "(foo >= 168100864 && foo <= 168118271) || (foo >= 2887057408 && foo <= 2887122943)",
  1014. Parameters: []EvaluationParameter{
  1015. EvaluationParameter{
  1016. Name: "foo",
  1017. Value: 2887057409,
  1018. },
  1019. },
  1020. Expected: true,
  1021. },
  1022. EvaluationTest{
  1023. Name: "Multiple comparator/logical operators, small value (#30)",
  1024. Input: "(foo >= 2887057408 && foo <= 2887122943) || (foo >= 168100864 && foo <= 168118271)",
  1025. Parameters: []EvaluationParameter{
  1026. EvaluationParameter{
  1027. Name: "foo",
  1028. Value: 168100865,
  1029. },
  1030. },
  1031. Expected: true,
  1032. },
  1033. EvaluationTest{
  1034. Name: "Multiple comparator/logical operators, small value, opposite order (#30)",
  1035. Input: "(foo >= 168100864 && foo <= 168118271) || (foo >= 2887057408 && foo <= 2887122943)",
  1036. Parameters: []EvaluationParameter{
  1037. EvaluationParameter{
  1038. Name: "foo",
  1039. Value: 168100865,
  1040. },
  1041. },
  1042. Expected: true,
  1043. },
  1044. EvaluationTest{
  1045. Name: "Incomparable array equality comparison",
  1046. Input: "arr == arr",
  1047. Parameters: []EvaluationParameter{
  1048. EvaluationParameter{
  1049. Name: "arr",
  1050. Value: []int{0, 0, 0},
  1051. },
  1052. },
  1053. Expected: true,
  1054. },
  1055. EvaluationTest{
  1056. Name: "Incomparable array not-equality comparison",
  1057. Input: "arr != arr",
  1058. Parameters: []EvaluationParameter{
  1059. EvaluationParameter{
  1060. Name: "arr",
  1061. Value: []int{0, 0, 0},
  1062. },
  1063. },
  1064. Expected: false,
  1065. },
  1066. EvaluationTest{
  1067. Name: "Mixed function and parameters",
  1068. Input: "sum(1.2, amount) + name",
  1069. Functions: map[string]ExpressionFunction{
  1070. "sum": func(arguments ...interface{}) (interface{}, error) {
  1071. sum := 0.0
  1072. for _, v := range arguments {
  1073. sum += v.(float64)
  1074. }
  1075. return sum, nil
  1076. },
  1077. },
  1078. Parameters: []EvaluationParameter{
  1079. EvaluationParameter{
  1080. Name: "amount",
  1081. Value: .8,
  1082. },
  1083. EvaluationParameter{
  1084. Name: "name",
  1085. Value: "awesome",
  1086. },
  1087. },
  1088. Expected: "2awesome",
  1089. },
  1090. EvaluationTest{
  1091. Name: "Short-circuit OR",
  1092. Input: "true || fail()",
  1093. Functions: map[string]ExpressionFunction{
  1094. "fail": func(arguments ...interface{}) (interface{}, error) {
  1095. return nil, errors.New("Did not short-circuit")
  1096. },
  1097. },
  1098. Expected: true,
  1099. },
  1100. EvaluationTest{
  1101. Name: "Short-circuit AND",
  1102. Input: "false && fail()",
  1103. Functions: map[string]ExpressionFunction{
  1104. "fail": func(arguments ...interface{}) (interface{}, error) {
  1105. return nil, errors.New("Did not short-circuit")
  1106. },
  1107. },
  1108. Expected: false,
  1109. },
  1110. EvaluationTest{
  1111. Name: "Short-circuit ternary",
  1112. Input: "true ? 1 : fail()",
  1113. Functions: map[string]ExpressionFunction{
  1114. "fail": func(arguments ...interface{}) (interface{}, error) {
  1115. return nil, errors.New("Did not short-circuit")
  1116. },
  1117. },
  1118. Expected: 1.0,
  1119. },
  1120. EvaluationTest{
  1121. Name: "Short-circuit coalesce",
  1122. Input: "'foo' ?? fail()",
  1123. Functions: map[string]ExpressionFunction{
  1124. "fail": func(arguments ...interface{}) (interface{}, error) {
  1125. return nil, errors.New("Did not short-circuit")
  1126. },
  1127. },
  1128. Expected: "foo",
  1129. },
  1130. EvaluationTest{
  1131. Name: "Simple parameter call",
  1132. Input: "foo.String",
  1133. Parameters: []EvaluationParameter{fooParameter},
  1134. Expected: fooParameter.Value.(dummyParameter).String,
  1135. },
  1136. EvaluationTest{
  1137. Name: "Simple parameter function call",
  1138. Input: "foo.Func()",
  1139. Parameters: []EvaluationParameter{fooParameter},
  1140. Expected: "funk",
  1141. },
  1142. EvaluationTest{
  1143. Name: "Simple parameter call from pointer",
  1144. Input: "fooptr.String",
  1145. Parameters: []EvaluationParameter{fooPtrParameter},
  1146. Expected: fooParameter.Value.(dummyParameter).String,
  1147. },
  1148. EvaluationTest{
  1149. Name: "Simple parameter function call from pointer",
  1150. Input: "fooptr.Func()",
  1151. Parameters: []EvaluationParameter{fooPtrParameter},
  1152. Expected: "funk",
  1153. },
  1154. EvaluationTest{
  1155. Name: "Simple parameter function call from pointer",
  1156. Input: "fooptr.Func3()",
  1157. Parameters: []EvaluationParameter{fooPtrParameter},
  1158. Expected: "fronk",
  1159. },
  1160. EvaluationTest{
  1161. Name: "Simple parameter call",
  1162. Input: "foo.String == 'hi'",
  1163. Parameters: []EvaluationParameter{fooParameter},
  1164. Expected: false,
  1165. },
  1166. EvaluationTest{
  1167. Name: "Simple parameter call with modifier",
  1168. Input: "foo.String + 'hi'",
  1169. Parameters: []EvaluationParameter{fooParameter},
  1170. Expected: fooParameter.Value.(dummyParameter).String + "hi",
  1171. },
  1172. EvaluationTest{
  1173. Name: "Simple parameter function call, two-arg return",
  1174. Input: "foo.Func2()",
  1175. Parameters: []EvaluationParameter{fooParameter},
  1176. Expected: "frink",
  1177. },
  1178. EvaluationTest{
  1179. Name: "Parameter function call with all argument types",
  1180. Input: "foo.TestArgs(\"hello\", 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1.0, 2.0, true)",
  1181. Parameters: []EvaluationParameter{fooParameter},
  1182. Expected: "hello: 33",
  1183. },
  1184. EvaluationTest{
  1185. Name: "Simple parameter function call, one arg",
  1186. Input: "foo.FuncArgStr('boop')",
  1187. Parameters: []EvaluationParameter{fooParameter},
  1188. Expected: "boop",
  1189. },
  1190. EvaluationTest{
  1191. Name: "Simple parameter function call, one arg",
  1192. Input: "foo.FuncArgStr('boop') + 'hi'",
  1193. Parameters: []EvaluationParameter{fooParameter},
  1194. Expected: "boophi",
  1195. },
  1196. EvaluationTest{
  1197. Name: "Nested parameter function call",
  1198. Input: "foo.Nested.Dunk('boop')",
  1199. Parameters: []EvaluationParameter{fooParameter},
  1200. Expected: "boopdunk",
  1201. },
  1202. EvaluationTest{
  1203. Name: "Nested parameter call",
  1204. Input: "foo.Nested.Funk",
  1205. Parameters: []EvaluationParameter{fooParameter},
  1206. Expected: "funkalicious",
  1207. },
  1208. EvaluationTest{
  1209. Name: "Parameter call with + modifier",
  1210. Input: "1 + foo.Int",
  1211. Parameters: []EvaluationParameter{fooParameter},
  1212. Expected: 102.0,
  1213. },
  1214. EvaluationTest{
  1215. Name: "Parameter string call with + modifier",
  1216. Input: "'woop' + (foo.String)",
  1217. Parameters: []EvaluationParameter{fooParameter},
  1218. Expected: "woopstring!",
  1219. },
  1220. EvaluationTest{
  1221. Name: "Parameter call with && operator",
  1222. Input: "true && foo.BoolFalse",
  1223. Parameters: []EvaluationParameter{fooParameter},
  1224. Expected: false,
  1225. },
  1226. EvaluationTest{
  1227. Name: "Null coalesce nested parameter",
  1228. Input: "foo.Nil ?? false",
  1229. Parameters: []EvaluationParameter{fooParameter},
  1230. Expected: false,
  1231. },
  1232. }
  1233. runEvaluationTests(evaluationTests, test)
  1234. }
  1235. /*
  1236. Tests the behavior of a nil set of parameters.
  1237. */
  1238. func TestNilParameters(test *testing.T) {
  1239. expression, _ := NewEvaluableExpression("true")
  1240. _, err := expression.Evaluate(nil)
  1241. if err != nil {
  1242. test.Fail()
  1243. }
  1244. }
  1245. /*
  1246. Tests functionality related to using functions with a struct method receiver.
  1247. Created to test #54.
  1248. */
  1249. func TestStructFunctions(test *testing.T) {
  1250. parseFormat := "2006"
  1251. y2k, _ := time.Parse(parseFormat, "2000")
  1252. y2k1, _ := time.Parse(parseFormat, "2001")
  1253. functions := map[string]ExpressionFunction{
  1254. "func1": func(args ...interface{}) (interface{}, error) {
  1255. return float64(y2k.Year()), nil
  1256. },
  1257. "func2": func(args ...interface{}) (interface{}, error) {
  1258. return float64(y2k1.Year()), nil
  1259. },
  1260. }
  1261. exp, _ := NewEvaluableExpressionWithFunctions("func1() + func2()", functions)
  1262. result, _ := exp.Evaluate(nil)
  1263. if result != 4001.0 {
  1264. test.Logf("Function calling method did not return the right value. Got: %v, expected %d\n", result, 4001)
  1265. test.Fail()
  1266. }
  1267. }
  1268. func runEvaluationTests(evaluationTests []EvaluationTest, test *testing.T) {
  1269. var expression *EvaluableExpression
  1270. var result interface{}
  1271. var parameters map[string]interface{}
  1272. var err error
  1273. fmt.Printf("Running %d evaluation test cases...\n", len(evaluationTests))
  1274. // Run the test cases.
  1275. for _, evaluationTest := range evaluationTests {
  1276. if evaluationTest.Functions != nil {
  1277. expression, err = NewEvaluableExpressionWithFunctions(evaluationTest.Input, evaluationTest.Functions)
  1278. } else {
  1279. expression, err = NewEvaluableExpression(evaluationTest.Input)
  1280. }
  1281. if err != nil {
  1282. test.Logf("Test '%s' failed to parse: '%s'", evaluationTest.Name, err)
  1283. test.Fail()
  1284. continue
  1285. }
  1286. parameters = make(map[string]interface{}, 8)
  1287. for _, parameter := range evaluationTest.Parameters {
  1288. parameters[parameter.Name] = parameter.Value
  1289. }
  1290. result, err = expression.Evaluate(parameters)
  1291. if err != nil {
  1292. test.Logf("Test '%s' failed", evaluationTest.Name)
  1293. test.Logf("Encountered error: %s", err.Error())
  1294. test.Fail()
  1295. continue
  1296. }
  1297. if result != evaluationTest.Expected {
  1298. test.Logf("Test '%s' failed", evaluationTest.Name)
  1299. test.Logf("Evaluation result '%v' does not match expected: '%v'", result, evaluationTest.Expected)
  1300. test.Fail()
  1301. }
  1302. }
  1303. }