gloo 6.7.2 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +1 -0
  3. data/docs/application.md +3 -1
  4. data/docs/iterators.md +39 -3
  5. data/docs/language_syntax.md +53 -5
  6. data/docs/objects.md +59 -2
  7. data/docs/operators.md +16 -0
  8. data/docs/plugins.md +9 -1
  9. data/docs/verbs.md +4 -2
  10. data/lib/VERSION +1 -1
  11. data/lib/VERSION_NOTES +24 -0
  12. data/lib/gloo/app/engine.rb +64 -8
  13. data/lib/gloo/app/log.rb +47 -6
  14. data/lib/gloo/convert/converter.rb +63 -2
  15. data/lib/gloo/convert/string_to_date.rb +14 -0
  16. data/lib/gloo/convert/string_to_datetime.rb +16 -0
  17. data/lib/gloo/convert/string_to_decimal.rb +13 -0
  18. data/lib/gloo/convert/string_to_integer.rb +9 -2
  19. data/lib/gloo/convert/string_to_time.rb +28 -0
  20. data/lib/gloo/core/error.rb +11 -2
  21. data/lib/gloo/core/event_manager.rb +7 -4
  22. data/lib/gloo/core/factory.rb +16 -11
  23. data/lib/gloo/core/gloo_system.rb +3 -2
  24. data/lib/gloo/core/here.rb +6 -1
  25. data/lib/gloo/core/invoker.rb +2 -3
  26. data/lib/gloo/core/not_found.rb +42 -0
  27. data/lib/gloo/core/obj.rb +30 -3
  28. data/lib/gloo/core/op.rb +2 -0
  29. data/lib/gloo/core/parser.rb +1 -1
  30. data/lib/gloo/core/pn.rb +33 -12
  31. data/lib/gloo/core/tokens.rb +38 -0
  32. data/lib/gloo/core/verb.rb +54 -0
  33. data/lib/gloo/exec/dispatch.rb +5 -6
  34. data/lib/gloo/exec/exec_env.rb +13 -0
  35. data/lib/gloo/exec/runner.rb +10 -2
  36. data/lib/gloo/exec/script.rb +13 -1
  37. data/lib/gloo/expr/expression.rb +39 -1
  38. data/lib/gloo/expr/op_plus.rb +3 -0
  39. data/lib/gloo/objs/basic/boolean.rb +16 -0
  40. data/lib/gloo/objs/basic/container.rb +154 -6
  41. data/lib/gloo/objs/basic/decimal.rb +1 -0
  42. data/lib/gloo/objs/basic/integer.rb +1 -0
  43. data/lib/gloo/objs/basic/string.rb +0 -1
  44. data/lib/gloo/objs/basic/string_msgs.rb +68 -32
  45. data/lib/gloo/objs/ctrl/each.rb +22 -3
  46. data/lib/gloo/objs/ctrl/each_dir.rb +20 -7
  47. data/lib/gloo/objs/ctrl/each_file.rb +78 -9
  48. data/lib/gloo/objs/ctrl/function.rb +6 -5
  49. data/lib/gloo/objs/dt/date.rb +28 -11
  50. data/lib/gloo/objs/dt/datetime.rb +72 -21
  51. data/lib/gloo/objs/dt/dt_tools.rb +46 -2
  52. data/lib/gloo/objs/dt/time.rb +28 -11
  53. data/lib/gloo/objs/str_utils/cipher.rb +17 -4
  54. data/lib/gloo/objs/str_utils/password.rb +6 -2
  55. data/lib/gloo/objs/system/erb.rb +9 -3
  56. data/lib/gloo/objs/system/file_handle.rb +57 -21
  57. data/lib/gloo/objs/system/system.rb +6 -2
  58. data/lib/gloo/objs/web/http_get.rb +12 -2
  59. data/lib/gloo/objs/web/http_post.rb +6 -2
  60. data/lib/gloo/objs/web/json.rb +38 -18
  61. data/lib/gloo/objs/web/uri.rb +36 -16
  62. data/lib/gloo/persist/file_loader.rb +72 -11
  63. data/lib/gloo/persist/file_saver.rb +2 -1
  64. data/lib/gloo/persist/indent_stack.rb +49 -33
  65. data/lib/gloo/persist/line_splitter.rb +40 -3
  66. data/lib/gloo/persist/persist_man.rb +2 -4
  67. data/lib/gloo/plugin/ext_manager.rb +6 -9
  68. data/lib/gloo/plugin/lib_manager.rb +2 -2
  69. data/lib/gloo/verbs/break.rb +1 -0
  70. data/lib/gloo/verbs/check.rb +1 -1
  71. data/lib/gloo/verbs/cls.rb +1 -0
  72. data/lib/gloo/verbs/create.rb +32 -4
  73. data/lib/gloo/verbs/execute.rb +1 -1
  74. data/lib/gloo/verbs/exists.rb +27 -15
  75. data/lib/gloo/verbs/files.rb +2 -1
  76. data/lib/gloo/verbs/help.rb +1 -0
  77. data/lib/gloo/verbs/if.rb +1 -1
  78. data/lib/gloo/verbs/invoke.rb +1 -1
  79. data/lib/gloo/verbs/list.rb +4 -3
  80. data/lib/gloo/verbs/load.rb +3 -3
  81. data/lib/gloo/verbs/move.rb +10 -8
  82. data/lib/gloo/verbs/put.rb +20 -8
  83. data/lib/gloo/verbs/quit.rb +1 -0
  84. data/lib/gloo/verbs/redirect.rb +8 -2
  85. data/lib/gloo/verbs/reload.rb +9 -1
  86. data/lib/gloo/verbs/run.rb +2 -2
  87. data/lib/gloo/verbs/save.rb +4 -2
  88. data/lib/gloo/verbs/tell.rb +1 -1
  89. data/lib/gloo/verbs/unless.rb +1 -1
  90. data/lib/gloo/verbs/unload.rb +8 -0
  91. data/test.gloo/ctrl/each.test.gloo +88 -0
  92. data/test.gloo/dt/datetime.test.gloo +5 -3
  93. data/test.gloo/lang/convert.test.gloo +1 -0
  94. data/test.gloo/lang/declaration.test.gloo +38 -0
  95. data/test.gloo/lang/exceptions.test.gloo +68 -0
  96. data/test.gloo/lang/failures.test.gloo +143 -0
  97. data/test.gloo/lang/gloo_sys.test.gloo +14 -9
  98. data/test.gloo/lang/it.test.gloo +139 -0
  99. data/test.gloo/lang/naming.test.gloo +21 -0
  100. data/test.gloo/lang/ops.test.gloo +30 -0
  101. data/test.gloo/math/div.test.gloo +2 -2
  102. data/test.gloo/math/mult.test.gloo +4 -4
  103. data/test.gloo/objs/can.test.gloo +104 -0
  104. data/test.gloo/objs/function.test.gloo +1 -0
  105. data/test.gloo/objs/string.test.gloo +24 -0
  106. data/test.gloo/string/str.test.gloo +28 -0
  107. data/test.gloo/verbs/check.test.gloo +10 -1
  108. data/test.gloo/verbs/create.test.gloo +37 -0
  109. data/test.gloo/verbs/exists.test.gloo +34 -1
  110. data/test.gloo/verbs/invoke.test.gloo +1 -0
  111. data/test.gloo/verbs/list.test.gloo +1 -0
  112. data/test.gloo/verbs/load.test.gloo +2 -0
  113. data/test.gloo/verbs/reload.test.gloo +1 -0
  114. data/test.gloo/verbs/save.test.gloo +2 -0
  115. data/test.gloo/verbs/throw.test.gloo +2 -0
  116. data/test.gloo/verbs/unload.test.gloo +35 -0
  117. metadata +4 -1
@@ -16,6 +16,7 @@ module Gloo
16
16
  # This will unload all loaded objects and reset the engine state.
17
17
  #
18
18
  def run
19
+ return if extra_words_err?( 'nothing was unloaded; to unload one object, use tell x to unload' )
19
20
  return unless @engine.persist_man.maps
20
21
 
21
22
  @engine.persist_man.unload_all
@@ -58,6 +59,13 @@ module Gloo
58
59
  'save state prior to executing.',
59
60
  :syntax => [ 'unload' ],
60
61
  :result => 'All objects will be unloaded from the heap.',
62
+ :errors => [
63
+ 'unload takes no object, so nothing was unloaded — anything ' \
64
+ 'written after unload is an error, and nothing is unloaded. ' \
65
+ 'To unload one object, use tell x to unload.'
66
+ ],
67
+ :notes => 'unload takes no object; to unload just one object, ' \
68
+ 'send it the unload message (tell x to unload).',
61
69
  :examples => <<~EXAMPLES.strip
62
70
  > unload
63
71
  EXAMPLES
@@ -35,3 +35,91 @@ tests.ctrl.each [can] :
35
35
  tell ^^.rpt to run
36
36
  eval ^^.count = 3
37
37
  assert "expected repeat to run 3 times"
38
+
39
+ #
40
+ # Each file / each dir over a nested folder tree.
41
+ # The tree is built under /tmp and removed at the end.
42
+ #
43
+
44
+ tests.ctrl.each_folder [can] :
45
+
46
+ count [int] : 0
47
+
48
+ make_tree [system] :
49
+ command [string] : rm -rf /tmp/gloo_each_walk && mkdir -p /tmp/gloo_each_walk/a/deep /tmp/gloo_each_walk/.obs && touch /tmp/gloo_each_walk/top.md /tmp/gloo_each_walk/notes.txt /tmp/gloo_each_walk/a/x.md /tmp/gloo_each_walk/a/deep/Y.MD /tmp/gloo_each_walk/.obs/o.md
50
+ get_output [bool] : false
51
+ remove_tree [system] :
52
+ command [string] : rm -rf /tmp/gloo_each_walk
53
+ get_output [bool] : false
54
+
55
+ files [each] :
56
+ file [file] :
57
+ in [file] : /tmp/gloo_each_walk
58
+ ext [string] : md
59
+ recursive [bool] : true
60
+ do [script] :
61
+ eval ^^.count + 1
62
+ put it into ^^.count
63
+
64
+ wild [each] :
65
+ file [file] :
66
+ in [file] : /tmp/gloo_each_walk/**/
67
+ ext [string] : md
68
+ do [script] :
69
+ eval ^^.count + 1
70
+ put it into ^^.count
71
+
72
+ top [each] :
73
+ file [file] :
74
+ in [file] : /tmp/gloo_each_walk
75
+ do [script] :
76
+ eval ^^.count + 1
77
+ put it into ^^.count
78
+
79
+ dirs [each] :
80
+ dir [file] :
81
+ in [file] : /tmp/gloo_each_walk
82
+ recursive [bool] : true
83
+ do [script] :
84
+ eval ^^.count + 1
85
+ put it into ^^.count
86
+
87
+ recursive_each_file [test] :
88
+ description [string] : Recursive each file finds nested files, ignoring ext case and hidden folders.
89
+ on_test [script] :
90
+ tell ^^.make_tree to run
91
+ put 0 into ^^.count
92
+ tell ^^.files to run
93
+ eval ^^.count = 3
94
+ assert "expected top.md, a/x.md and a/deep/Y.MD"
95
+ tell ^^.remove_tree to run
96
+
97
+ wildcard_in_folder [test] :
98
+ description [string] : A ** wildcard in the folder walks the tree too.
99
+ on_test [script] :
100
+ tell ^^.make_tree to run
101
+ put 0 into ^^.count
102
+ tell ^^.wild to run
103
+ eval ^^.count = 3
104
+ assert "expected the same 3 files as recursive"
105
+ tell ^^.remove_tree to run
106
+
107
+ files_only_by_default [test] :
108
+ description [string] : Each file without ext lists files, not folders.
109
+ on_test [script] :
110
+ tell ^^.make_tree to run
111
+ put 0 into ^^.count
112
+ tell ^^.top to run
113
+ eval ^^.count = 2
114
+ assert "expected top.md and notes.txt, but not the folder a"
115
+ tell ^^.remove_tree to run
116
+
117
+ recursive_each_dir [test] :
118
+ description [string] : Recursive each dir lists every folder in the tree, skipping hidden ones.
119
+ on_test [script] :
120
+ tell ^^.make_tree to run
121
+ put 0 into ^^.count
122
+ tell ^^.dirs to run
123
+ eval ^^.count = 2
124
+ assert "expected a and a/deep"
125
+ tell ^^.remove_tree to run
@@ -5,15 +5,17 @@
5
5
  tests.dt.datetime [can] :
6
6
 
7
7
  d [dt] :
8
+ # Only the now test uses this; the others set d, and tests run in random order.
9
+ fresh [dt] :
8
10
 
9
11
  now [test] :
10
12
  description [string] : Get the current datetime
11
13
  on_test [script] :
12
- check ^^.d for blank?
14
+ check ^^.fresh for blank?
13
15
  assert "expected new datetime to be blank"
14
16
 
15
- tell ^^.d to now
16
- check ^^.d for blank?
17
+ tell ^^.fresh to now
18
+ check ^^.fresh for blank?
17
19
  refute "expected datetime to not be blank after now"
18
20
 
19
21
  is_today [test] :
@@ -13,6 +13,7 @@ tests.lang.convert [can] :
13
13
 
14
14
  string_to_number [test] :
15
15
  description [string] : a string is converted to the leading number for an int or decimal target.
16
+ expect_errors [bool] : true
16
17
  on_test [script] :
17
18
  put '3 third time' into ^^.i
18
19
  eval ^^.i = 3
@@ -0,0 +1,38 @@
1
+ #
2
+ # Declaration syntax tests
3
+ #
4
+
5
+ tests.lang.declaration [can] :
6
+
7
+ # A fixture deliberately missing the ']' after its type, loaded inside
8
+ # its test so the syntax error it reports belongs to that test.
9
+ # Loading reads the type as 'int' anyway.
10
+ unclosed_file [file] : /tmp/gloo_declaration_test.gloo
11
+ unclosed_src [text] : BEGIN
12
+ declaration_fixture [container] :
13
+ unclosed [int : 7
14
+ END
15
+
16
+ closed_then_colon [int]: 8
17
+
18
+ type_missing_its_closing_bracket [test] :
19
+ description [string] : a type missing its ']' is still read as that type, with its value.
20
+ expect_errors [bool] : true
21
+ on_test [script] :
22
+ tell ^^.unclosed_file to write ( ^^.unclosed_src )
23
+ load /tmp/gloo_declaration_test.gloo
24
+ eval declaration_fixture.unclosed = 7
25
+ assert "expected the value 7"
26
+
27
+ put '12' into declaration_fixture.unclosed
28
+ eval declaration_fixture.unclosed = 12
29
+ assert "expected it to still be an integer"
30
+
31
+ tell declaration_fixture to unload
32
+ tell ^^.unclosed_file to delete
33
+
34
+ closed_type_followed_by_the_colon [test] :
35
+ description [string] : a ']' followed directly by ':' is a normal declaration.
36
+ on_test [script] :
37
+ eval ^^.closed_then_colon = 8
38
+ assert "expected the value 8"
@@ -20,9 +20,12 @@ tests.lang.exceptions [can] :
20
20
  error_data [can] :
21
21
  message [string] :
22
22
  backtrace [string] :
23
+ kind [string] :
24
+ location [string] :
23
25
 
24
26
  confirm_throw_fires_on_exception [test] :
25
27
  description [string] : Confirm throw fires on_exception and populates exception_data, but does not fire on_error.
28
+ expect_errors [bool] : true
26
29
  on_test [script] :
27
30
  put false into ^^.exc_fired
28
31
  put false into ^^.err_fired
@@ -40,6 +43,7 @@ tests.lang.exceptions [can] :
40
43
 
41
44
  confirm_execution_continues_after_throw [test] :
42
45
  description [string] : Confirm execution continues with the next line after a throw.
46
+ expect_errors [bool] : true
43
47
  on_test [script] :
44
48
  throw "boom"
45
49
  eval 2 + 2
@@ -48,6 +52,7 @@ tests.lang.exceptions [can] :
48
52
 
49
53
  confirm_on_error_still_works [test] :
50
54
  description [string] : Confirm on_error still fires for a genuine gloo-level error, independent of on_exception.
55
+ expect_errors [bool] : true
51
56
  on_test [script] :
52
57
  put false into ^^.exc_fired
53
58
  put false into ^^.err_fired
@@ -59,3 +64,66 @@ tests.lang.exceptions [can] :
59
64
 
60
65
  eval ^^.exc_fired = false
61
66
  assert "expected on_exception to NOT have fired for a gloo-level error"
67
+
68
+ confirm_missing_value_fires_on_error [test] :
69
+ description [string] : Using a missing object as a value is a runtime error, but asking whether it exists is not.
70
+ expect_errors [bool] : true
71
+ on_test [script] :
72
+ put false into ^^.err_fired
73
+ exists? instance ^^.no_such_value
74
+ eval ^^.err_fired = false
75
+ assert "expected exists? on a missing object to not fire on_error"
76
+
77
+ show ^^.no_such_value
78
+ eval ^^.err_fired = true
79
+ assert "expected using a missing object as a value to fire on_error"
80
+
81
+ eval ^^.error_data.message = "Object '^^.no_such_value' was not found."
82
+ assert "expected error_data.message to name the missing object"
83
+
84
+ kept [string] : keep me
85
+
86
+ confirm_put_of_missing_value_keeps_target [test] :
87
+ description [string] : A put whose value uses a missing object leaves the target unchanged.
88
+ expect_errors [bool] : true
89
+ on_test [script] :
90
+ put 'keep me' into ^^.kept
91
+ put ^^.no_such_value into ^^.kept
92
+ eval ^^.kept = 'keep me'
93
+ assert "expected the target to keep its value"
94
+
95
+ confirm_error_kind [test] :
96
+ description [string] : error_data.kind says whether the command couldn't be understood (syntax) or couldn't be done (runtime).
97
+ expect_errors [bool] : true
98
+ on_test [script] :
99
+ nosuchverb
100
+ eval ^^.error_data.kind = 'syntax'
101
+ assert "expected an unknown verb to be a syntax error"
102
+
103
+ show ^^.no_such_value
104
+ eval ^^.error_data.kind = 'runtime'
105
+ assert "expected a missing object to be a runtime error"
106
+
107
+ confirm_error_location [test] :
108
+ description [string] : error_data.location names the script and line that failed.
109
+ expect_errors [bool] : true
110
+ on_test [script] :
111
+ show 1
112
+ show ^^.no_such_value
113
+ eval ^^.error_data.location = 'tests.lang.exceptions.confirm_error_location.on_test, line 2'
114
+ assert "expected the location of the failed line"
115
+
116
+ num [int] : 5
117
+
118
+ confirm_bad_value_is_a_warning [test] :
119
+ description [string] : A value that isn't really of its type is a warning, not an error; the best guess is used.
120
+ expect_errors [bool] : true
121
+ on_test [script] :
122
+ put false into ^^.err_fired
123
+ put 'x' into ^^.num
124
+ eval ^^.err_fired = false
125
+ assert "expected a bad value to not fire on_error"
126
+
127
+ eval ^^.num = 0
128
+ assert "expected the best guess, 0"
129
+
@@ -0,0 +1,143 @@
1
+ #
2
+ # Predictable failures tests
3
+ #
4
+ # A failure a script can cause with ordinary bad input or the outside
5
+ # world (invalid JSON, a template with a mistake, a value that isn't a
6
+ # date) is a gloo runtime error: it fires on_error (not on_exception),
7
+ # reads "Could not ...", and a command that would set it sets it false.
8
+ #
9
+
10
+ tests.lang.failures [can] :
11
+
12
+ err_fired [bool] : false
13
+ exc_fired [bool] : false
14
+
15
+ on_error [script] :
16
+ put true into ^.err_fired
17
+
18
+ on_exception [script] :
19
+ put true into ^.exc_fired
20
+
21
+ error_data [can] :
22
+ message [string] :
23
+ backtrace [string] :
24
+ kind [string] :
25
+ location [string] :
26
+
27
+ j [json] : {bad
28
+ e [erb] :
29
+ template [text] : <%= 1 + %>
30
+ params [can] :
31
+ result [text] :
32
+ d [date] :
33
+ msg [string] :
34
+
35
+ invalid_json_is_an_error [test] :
36
+ description [string] : Pretty-printing invalid JSON fires on_error, not on_exception, and it is false.
37
+ expect_errors [bool] : true
38
+ on_test [script] :
39
+ put false into ^^.err_fired
40
+ put false into ^^.exc_fired
41
+ tell ^^.j to pretty
42
+ eval it = false
43
+ assert "expected it to be false"
44
+
45
+ eval ^^.err_fired = true
46
+ assert "expected on_error to fire"
47
+ eval ^^.exc_fired = false
48
+ assert "expected on_exception not to fire"
49
+
50
+ put ^^.error_data.message into ^^.msg
51
+ check ^^.msg for starts_with? ( 'Could not parse the JSON in tests.lang.failures.j:' )
52
+ assert "expected the error to say what couldn't be done"
53
+
54
+ template_syntax_error_is_an_error [test] :
55
+ description [string] : An erb template with a syntax error is a gloo error, and gloo keeps running.
56
+ expect_errors [bool] : true
57
+ on_test [script] :
58
+ put false into ^^.err_fired
59
+ put false into ^^.exc_fired
60
+ run ^^.e
61
+ eval it = false
62
+ assert "expected it to be false"
63
+
64
+ eval ^^.err_fired = true
65
+ assert "expected on_error to fire"
66
+ eval ^^.exc_fired = false
67
+ assert "expected on_exception not to fire"
68
+
69
+ not_a_date_is_an_error [test] :
70
+ description [string] : A date message on a value that isn't a date fires on_error, and it is false.
71
+ expect_errors [bool] : true
72
+ on_test [script] :
73
+ put '' into ^^.d
74
+ put false into ^^.err_fired
75
+ put false into ^^.exc_fired
76
+ tell ^^.d to mm
77
+ eval it = false
78
+ assert "expected it to be false"
79
+
80
+ eval ^^.err_fired = true
81
+ assert "expected on_error to fire"
82
+ eval ^^.exc_fired = false
83
+ assert "expected on_exception not to fire"
84
+
85
+ unknown_unit_is_an_error [test] :
86
+ description [string] : Adding an amount with an unknown unit fires on_error, and it is false.
87
+ expect_errors [bool] : true
88
+ on_test [script] :
89
+ put '2026-01-01' into ^^.d
90
+ put false into ^^.err_fired
91
+ tell ^^.d to add ( '3 dayz' )
92
+ eval it = false
93
+ assert "expected it to be false"
94
+
95
+ eval ^^.err_fired = true
96
+ assert "expected on_error to fire"
97
+
98
+ eval ^^.error_data.message = "'3 dayz' is not an amount like '1 day' or '3 months'."
99
+ assert "expected the error to show what an amount looks like"
100
+
101
+ #
102
+ # Chronic phrases are still dates: the checks above only catch a value
103
+ # that couldn't be read as one.
104
+ #
105
+
106
+ tests.lang.chronic_phrases [can] :
107
+
108
+ d [date] :
109
+ now [date] :
110
+ dt [datetime] :
111
+ a [string] :
112
+ b [string] :
113
+
114
+ today_is_a_date [test] :
115
+ description [string] : put 'today' into a date gives today's date.
116
+ on_test [script] :
117
+ put 'today' into ^^.d
118
+ tell ^^.now to now
119
+ tell ^^.d to format ( '%Y-%m-%d' )
120
+ put it into ^^.a
121
+ tell ^^.now to format ( '%Y-%m-%d' )
122
+ put it into ^^.b
123
+ eval ^^.a = ^^.b
124
+ assert "expected 'today' to be today's date"
125
+
126
+ add_to_a_phrase [test] :
127
+ description [string] : A date set from a phrase can be added to.
128
+ on_test [script] :
129
+ put '2026-01-01' into ^^.d
130
+ tell ^^.d to add ( '1 week' )
131
+ eval it = '2026.01.08'
132
+ assert "expected a week later"
133
+
134
+ tomorrow_is_in_the_future [test] :
135
+ description [string] : 'tomorrow at 3pm' in a datetime is tomorrow, and in the future.
136
+ on_test [script] :
137
+ put 'tomorrow at 3pm' into ^^.dt
138
+ tell ^^.dt to is_tomorrow
139
+ assert "expected it to be tomorrow"
140
+ tell ^^.dt to is_future
141
+ assert "expected it to be in the future"
142
+ tell ^^.dt to is_past
143
+ refute "expected it not to be in the past"
@@ -4,6 +4,9 @@
4
4
 
5
5
  tests.lang.gloo_sys [can] :
6
6
 
7
+ # How many of the platform_*? checks are true.
8
+ platforms [int] : 0
9
+
7
10
  get_platform_info [test] :
8
11
  description [string] : Get the platform info.
9
12
  on_test [script] :
@@ -89,16 +92,18 @@ tests.lang.gloo_sys [can] :
89
92
  assert "expected screen_cols to be greater than 0"
90
93
 
91
94
  get_platform_os [test] :
92
- description [string] : Get the platform OS.
95
+ description [string] : Exactly one of mac, windows and linux is true, on any platform.
93
96
  on_test [script] :
94
- eval $.platform_mac? = true
95
- assert "expected platform_mac to be true"
97
+ put 0 into ^^.platforms
98
+
99
+ eval $.platform_mac?
100
+ if it then tell ^^.platforms to inc
96
101
 
97
- eval $.platform_windows? = false
98
- assert "expected platform_windows to be false"
102
+ eval $.platform_windows?
103
+ if it then tell ^^.platforms to inc
99
104
 
100
- eval $.platform_linux? = false
101
- assert "expected platform_linux to be false"
105
+ eval $.platform_linux?
106
+ if it then tell ^^.platforms to inc
102
107
 
103
- eval $.platform_wsl? = false
104
- assert "expected platform_wsl to be false"
108
+ eval ^^.platforms = 1
109
+ assert "expected exactly one of platform_mac, platform_windows and platform_linux to be true"
@@ -37,3 +37,142 @@ tests.lang.it [can] :
37
37
  list ^^.n
38
38
  eval it = 42
39
39
  assert "expected it to survive statements that don't produce a result"
40
+
41
+ #
42
+ # it is a read-only result value, not an object: it can be read, but
43
+ # nothing can target it.
44
+ #
45
+
46
+ tests.lang.it_read_only [can] :
47
+
48
+ err_fired [bool] : false
49
+ s [string] :
50
+
51
+ on_error [script] :
52
+ put true into ^.err_fired
53
+
54
+ error_data [can] :
55
+ message [string] :
56
+ backtrace [string] :
57
+ kind [string] :
58
+ location [string] :
59
+
60
+ message_to_it_is_an_error [test] :
61
+ description [string] : Sending it a message reports an error that says what to do instead.
62
+ expect_errors [bool] : true
63
+ on_test [script] :
64
+ put false into ^^.err_fired
65
+ eval 'abc'
66
+ check it for starts_with? ( 'a' )
67
+ eval ^^.err_fired = true
68
+ assert "expected check it for ... to be an error"
69
+
70
+ eval ^^.error_data.message = "it isn't an object and can't be sent messages; put it into an object first (put it into x)."
71
+ assert "expected the error to say to put it into an object first"
72
+
73
+ put_into_it_is_an_error [test] :
74
+ description [string] : Putting a value into it is an error.
75
+ expect_errors [bool] : true
76
+ on_test [script] :
77
+ put false into ^^.err_fired
78
+ put 'x' into it
79
+ eval ^^.err_fired = true
80
+ assert "expected put into it to be an error"
81
+
82
+ put_it_into_an_object_works [test] :
83
+ description [string] : The workaround: put it into an object, then send that object the message.
84
+ on_test [script] :
85
+ put false into ^^.err_fired
86
+ eval 'abc'
87
+ put it into ^^.s
88
+ check ^^.s for starts_with? ( 'a' )
89
+ assert "expected the message to work on the object"
90
+
91
+ eval ^^.err_fired = false
92
+ assert "expected no error"
93
+
94
+ #
95
+ # The it rule: a command that works out a value puts it in it; when
96
+ # something goes wrong, it is false.
97
+ #
98
+
99
+ tests.lang.it_rule [can] :
100
+
101
+ err_fired [bool] : false
102
+ s [string] :
103
+ kept [string] : keep me
104
+
105
+ on_error [script] :
106
+ put true into ^.err_fired
107
+
108
+ failed_put_sets_it_false [test] :
109
+ description [string] : A put whose value can't be worked out sets it to false and keeps the target.
110
+ expect_errors [bool] : true
111
+ on_test [script] :
112
+ put false into ^^.err_fired
113
+ eval 'before'
114
+ put ^^.no_such_value into ^^.kept
115
+ eval it = false
116
+ assert "expected it to be false after a failed put"
117
+
118
+ eval ^^.kept = 'keep me'
119
+ assert "expected the target to keep its value"
120
+
121
+ eval ^^.err_fired = true
122
+ assert "expected the failed put to be an error"
123
+
124
+ put_into_missing_object_sets_it_false [test] :
125
+ description [string] : A put into an object that doesn't exist sets it to false.
126
+ expect_errors [bool] : true
127
+ on_test [script] :
128
+ eval 'before'
129
+ put 'x' into ^^.no_such_target
130
+ eval it = false
131
+ assert "expected it to be false"
132
+
133
+ value_messages_set_it [test] :
134
+ description [string] : A message that changes its own value also puts the new value in it.
135
+ on_test [script] :
136
+ put ' padded ' into ^^.s
137
+ tell ^^.s to trim
138
+ eval it = 'padded'
139
+ assert "expected trim to put the trimmed string in it"
140
+
141
+ put 'a b' into ^^.s
142
+ tell ^^.s to format_for_html
143
+ eval it = 'a b'
144
+ assert "expected format_for_html to put the result in it"
145
+
146
+ failed_function_sets_it_false [test] :
147
+ description [string] : A function whose on_invoke fails sets it to false.
148
+ expect_errors [bool] : true
149
+ on_test [script] :
150
+ eval 'before'
151
+ invoke ^^.failing
152
+ eval it = false
153
+ assert "expected it to be false after a failed invoke"
154
+
155
+ failing [ƒ] :
156
+ params [can] :
157
+ on_invoke [script] :
158
+ put 1 into no.such.object
159
+ result [int] :
160
+
161
+ message_to_missing_object_sets_it_false [test] :
162
+ description [string] : A message that can't be delivered leaves no stale value in it.
163
+ expect_errors [bool] : true
164
+ on_test [script] :
165
+ eval 'stale'
166
+ tell ^^.no_such_obj to get_parent
167
+ eval it = false
168
+ assert "expected it to be false after a message to a missing object"
169
+
170
+ eval 'stale'
171
+ tell ^^.s to no_such_msg
172
+ eval it = false
173
+ assert "expected it to be false after a message the object doesn't take"
174
+
175
+ eval 'stale'
176
+ run ^^.no_such_obj
177
+ eval it = false
178
+ assert "expected it to be false after running a missing object"
@@ -14,6 +14,14 @@ tests.lang.naming [can] :
14
14
  string [string] : a type keyword as a name
15
15
  put [string] : a verb keyword as a name
16
16
 
17
+ # A fixture with a deliberately nameless object, loaded inside its
18
+ # test so the syntax error it reports belongs to that test.
19
+ nameless_file [file] : /tmp/gloo_naming_test.gloo
20
+ nameless_src [text] : BEGIN
21
+ naming_fixture [container] :
22
+ [string] : no name given
23
+ END
24
+
17
25
  first_of_a_duplicate_name_wins [test] :
18
26
  description [string] : when two siblings share a name, the path always reaches the first.
19
27
  on_test [script] :
@@ -40,3 +48,16 @@ tests.lang.naming [can] :
40
48
 
41
49
  eval ^^.put = 'a verb keyword as a name'
42
50
  assert "expected a verb keyword to be usable as a name"
51
+
52
+ declaration_with_no_name_gets_a_placeholder [test] :
53
+ description [string] : a declaration with no name loads under the placeholder name unnamed_1.
54
+ expect_errors [bool] : true
55
+ on_test [script] :
56
+ tell ^^.nameless_file to write ( ^^.nameless_src )
57
+ load /tmp/gloo_naming_test.gloo
58
+ eval naming_fixture.unnamed_1 = 'no name given'
59
+ assert "expected the nameless object to be reached as unnamed_1"
60
+
61
+ tell naming_fixture to unload
62
+ tell ^^.nameless_file to delete
63
+
@@ -4,6 +4,10 @@
4
4
 
5
5
  tests.lang.ops [can] :
6
6
 
7
+ first [string] : Ada
8
+ last [string] : Lovelace
9
+ full [string] :
10
+
7
11
  confirm_eq_ops [test] :
8
12
  description [string] : Confirm equality operations.
9
13
  on_test [script] :
@@ -75,3 +79,29 @@ tests.lang.ops [can] :
75
79
 
76
80
  eval 1 <= 1
77
81
  assert "expected 1 <= 1"
82
+
83
+ confirm_and_joins [test] :
84
+ description [string] : and joins values, the same as +.
85
+ on_test [script] :
86
+ put ^^.first and ' ' and ^^.last into ^^.full
87
+ eval ^^.full = 'Ada Lovelace'
88
+ assert "expected first and ' ' and last to be 'Ada Lovelace'"
89
+
90
+ eval 2 and 3 = 5
91
+ assert "expected 2 and 3 to be 5"
92
+
93
+ confirm_join_without_operator [test] :
94
+ description [string] : Values side by side with no operator between them are joined.
95
+ on_test [script] :
96
+ put 'hello ' 'world' into ^^.full
97
+ eval ^^.full = 'hello world'
98
+ assert "expected 'hello ' 'world' to be 'hello world'"
99
+
100
+ put ^^.first ' ' ^^.last into ^^.full
101
+ eval ^^.full = 'Ada Lovelace'
102
+ assert "expected first ' ' last to be 'Ada Lovelace'"
103
+
104
+ put 'a' 'b' + 'c' and 'd' into ^^.full
105
+ eval ^^.full = 'abcd'
106
+ assert "expected no operator, + and and to mix"
107
+
@@ -28,8 +28,8 @@ tests.math.div [can] :
28
28
  eval 10.5 / 5 = 2.1
29
29
  assert 'expected 10.5 / 5 = 2.1'
30
30
 
31
- eval 100.5 / 200.5 = 0.5
32
- assert 'expected 100.5 / 200.5 = 0.5'
31
+ eval 100.5 / 201 = 0.5
32
+ assert 'expected 100.5 / 201 = 0.5'
33
33
 
34
34
  eval 1.5 / 10.5 = 0.1
35
35
  refute 'expected 1.5 / 10.5 != 0.1'