Просмотр исходного кода

Sol Part 53: You Should Get Yourself Tested!

master
Graham Northup 5 лет назад
Родитель
Сommit
5fe1f453a7
Подписано: grissess Идентификатор GPG ключа: 5D000E6F539376FB
  1. 40
      builtins.c
  2. 21
      object.c
  3. 53
      sol.h
  4. 1
      state.c
  5. 16
      tests/_lib.sol
  6. 38
      tests/basic_control.sol
  7. 9
      tests/basic_equality.sol
  8. 7
      tests/basic_len.sol
  9. 10
      tests/basic_vars.sol
  10. 5
      tests/bt_range.sol
  11. 0
      tests/fails.sol.disabled
  12. 4
      tests/lang_forex.sol
  13. 4
      tests/lang_ifex.sol
  14. 47
      tests/lang_scoping.sol

40
builtins.c

@ -1085,6 +1085,46 @@ sol_object_t *sol_f_list_mul(sol_state_t *state, sol_object_t *args) {
return ls;
}
sol_object_t *sol_f_list_cmp(sol_state_t *state, sol_object_t *args) {
sol_object_t *a = sol_list_get_index(state, args, 0), *b = sol_list_get_index(state, args, 1), *item, *ls, *tmp;
int i, alen, blen;
if(!sol_is_list(b)) {
sol_obj_free(b);
sol_obj_free(a);
return sol_new_int(state, 1); // XXX lists always greater than non-lists?
}
alen = sol_list_len(state, a);
blen = sol_list_len(state, b);
if(alen != blen) {
sol_obj_free(b);
sol_obj_free(a);
return sol_new_int(state, alen > blen ? 1 : -1);
}
ls = sol_new_list(state);
sol_list_insert(state, ls, 0, state->None);
sol_list_insert(state, ls, 1, state->None);
for(i = 0; i < alen; i++) {
tmp = sol_list_get_index(state, a, i);
sol_list_set_index(state, ls, 0, tmp);
item = sol_list_get_index(state, b, i);
sol_list_set_index(state, ls, 1, item);
sol_obj_free(item);
item = CALL_METHOD(state, tmp, cmp, ls);
sol_obj_free(tmp);
if(item->ival != 0) {
sol_obj_free(ls);
sol_obj_free(b);
sol_obj_free(a);
return item;
}
sol_obj_free(item);
}
sol_obj_free(ls);
sol_obj_free(b);
sol_obj_free(a);
return sol_new_int(state, 0);
}
sol_object_t *sol_f_list_index(sol_state_t *state, sol_object_t *args) {
sol_object_t *ls = sol_list_get_index(state, args, 0), *b = sol_list_get_index(state, args, 1), *ival;
sol_object_t *res, *funcs;

21
object.c

@ -308,6 +308,14 @@ int sol_map_len(sol_state_t *state, sol_object_t *map) {
return dsl_seq_len(map->seq);
}
sol_object_t *sol_map_mcell_index(sol_state_t *state, sol_object_t *map, int index) {
sol_object_t *res = dsl_seq_get(map->seq, index);
if(res) {
return sol_incref(res);
}
return sol_incref(state->None);
}
sol_object_t *sol_map_mcell(sol_state_t *state, sol_object_t *map, sol_object_t *key) {
sol_object_t *list, *cmp, *icmp, *res = NULL;
dsl_seq_iter *iter;
@ -375,6 +383,19 @@ sol_object_t *sol_map_get_name(sol_state_t *state, sol_object_t *map, char *name
void sol_map_set(sol_state_t *state, sol_object_t *map, sol_object_t *key, sol_object_t *val) {
sol_object_t *mcell = sol_map_mcell(state, map, key), *newcell, *temp;
if(sol_is_none(state, val)) {
if(!sol_is_none(state, mcell)) {
// XXX hacky
dsl_seq_iter *iter = dsl_new_seq_iter(map->seq);
while(!dsl_seq_iter_is_invalid(iter)) {
if(mcell == dsl_seq_iter_at(iter)) {
dsl_seq_iter_delete_at(iter);
break;
}
}
}
return;
}
if(sol_is_none(state, mcell)) {
newcell = sol_alloc_object(state);
newcell->type = SOL_MCELL;

53
sol.h

@ -765,6 +765,7 @@ sol_object_t *sol_f_str_find(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_list_add(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_list_mul(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_list_cmp(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_list_index(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_list_setindex(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_list_len(sol_state_t *, sol_object_t *);
@ -931,28 +932,80 @@ void sol_list_append(sol_state_t *, sol_object_t *, sol_object_t *);
* list. */
#define sol_list_pop(st, ls) sol_list_remove(st, ls, 0);
/** Creates a new empty Sol map. */
sol_object_t *sol_new_map(sol_state_t *);
/** Internal routine to get the length (number of associations) in a Sol map. */
int sol_map_len(sol_state_t *, sol_object_t *);
/** Internal routine to get an MCELL by index.
*
* This is most typically used to iterate over the associations in a map in an
* arbitrary order.
*/
sol_object_t *sol_map_mcell_index(sol_state_t *, sol_object_t *, int);
/** Internal routine to get an MCELL with key equal to `key`, or `None`.
*
* This does most of the work of association lookup; many other functions in
* the map internal API are built upon this one.
*/
sol_object_t *sol_map_mcell(sol_state_t *, sol_object_t *, sol_object_t *);
/** Internal routine to determine if a key is in a map. */
int sol_map_has(sol_state_t *, sol_object_t *, sol_object_t *);
/** Internal routine to get the value associated with a key in a map, or `None`
* if there is no association.. */
sol_object_t *sol_map_get(sol_state_t *, sol_object_t *, sol_object_t *);
/** Internal routine to get the value associated with a string key (specified
* as a C string) in a map, or `None` if there is no association. */
sol_object_t *sol_map_get_name(sol_state_t *, sol_object_t *, char *);
/** Internal routine to set an association in a map.
*
* If the key had a previous association, it is lost. If the value is `None`,
* any existing association is deleted; this is consistent with a return of
* `None` for any map get for which no association exists.
*/
void sol_map_set(sol_state_t *, sol_object_t *, sol_object_t *, sol_object_t *);
/** Internal routine to set an association, borrowing a reference to the value
* instead of owning a reference.
*
* This is mostly used in `sol_state_init`, where it avoids having to
* `sol_obj_free` after creating a new object just to associate it.
*
* Note that this actually decrements the reference count on your pointer--the
* map still increments the reference count, but this effect is nullified, so
* it works out the same way.
*/
#define sol_map_borrow(state, map, key, object) do {\
sol_object_t *__obj = (object);\
sol_map_set((state), (map), (key), __obj);\
sol_obj_free(__obj);\
} while(0)
/** Internal routine to set a map association with a C-string key. */
void sol_map_set_name(sol_state_t *, sol_object_t *, char *, sol_object_t *);
/** Internal routine to set a map association with a C-string key, and
* borrowing a reference to the value. */
#define sol_map_borrow_name(state, map, str, object) do {\
sol_object_t *__obj = (object);\
sol_map_set_name((state), (map), (str), __obj);\
sol_obj_free(__obj);\
} while(0)
/** Internal routine to set a map associaiton to a new value only if the key
* was associated with a value (other than `None`) previously.
*
* This is mostly used in the end of `sol_f_func_call` to update the closure.
*/
void sol_map_set_existing(sol_state_t *, sol_object_t *, sol_object_t *, sol_object_t *);
/** Creates a new copy of an existing Sol map. */
sol_object_t *sol_map_copy(sol_state_t *, sol_object_t *);
/** Merges the associations of the source map into the destination map.
*
* Associations in the source map take precedence if the same key exists in
* both.
*/
void sol_map_merge(sol_state_t *, sol_object_t *, sol_object_t *);
/** Merges the associations of the source map into the destination map, but
* only for keys already in the destination map. */
void sol_map_merge_existing(sol_state_t *, sol_object_t *, sol_object_t *);
/** Updates a map to contain an association from value to key for every key and
* value already within. */
void sol_map_invert(sol_state_t *, sol_object_t *);
// Defined in ast.h

1
state.c

@ -126,6 +126,7 @@ int sol_state_init(sol_state_t *state) {
state->ListOps.tname = "list";
state->ListOps.add = sol_f_list_add;
state->ListOps.mul = sol_f_list_mul;
state->ListOps.cmp = sol_f_list_cmp;
state->ListOps.call = sol_f_not_impl;
state->ListOps.index = sol_f_list_index;
state->ListOps.setindex = sol_f_list_setindex;

16
tests/_lib.sol

@ -1,19 +1,27 @@
func assert(x, msg)
_test_count += 1
io.stdout:write('Test ' + tostring(_test_count) + ': ' + msg)
if !x then
error("Assertion failed: " + tostring(msg))
end
print("...passed")
end
assert.closure._test_count = 0
func assert_eq(x, y, msg)
assert(x == y, msg)
assert(x == y, "equality: " + tostring(x) + " == " + tostring(y) + ": " + msg)
end
func assert_neq(x, y, msg)
assert(x != y, "inequality: " + tostring(x) + " != " + tostring(y) + ": " + msg)
end
func assert_none(x, msg)
assert_eq(None, x, msg)
assert(None == x, "None: " + tostring(x) + ": " + msg)
end
func assert_not_none(x, msg)
assert(None != x, msg)
assert(None != x, "Not None: " + tostring(x) + ": " + msg)
end
func warn(msg)
@ -25,5 +33,3 @@ func warn_if(cond, msg)
warn(msg)
end
end
print("(test library loaded successfully)")

38
tests/basic_control.sol

@ -0,0 +1,38 @@
execfile("tests/_lib.sol")
if 0 then
error("Iftrue on a false constant")
else
print("Iffalse on a false contant (pass)")
end
if 1 then
print("iftrue on a true constant (pass)")
else
error("iffalse on a true constant")
end
iters = 0
for i in range(5) do iters += 1 end
assert_eq(iters, 5, "for over range")
func count(start, stop, step)
if None == step then step = 1 end
func inner()
if i >= stop then return StopIteration end
i += step
return i - 1
end
inner.closure.stop = stop
inner.closure.i = start
inner.closure.step = step
return inner
end
iters = 0
for i in count(0, 10) do iters += 1 end
assert_eq(iters, 10, "for over iterator function")
iters = 0
for i in count(0, 10, 2) do iters += 1 end
assert_eq(iters, 5, "for over iterator function")

9
tests/basic_equality.sol

@ -0,0 +1,9 @@
execfile("tests/_lib.sol")
assert_eq(1, 1, "int cmp")
assert_eq(-3695, -3695, "int cmp")
assert_eq([1, 2, 3], [1, 2, 3], "list cmp")
assert_neq([1, 2, 3], [4, 5, 6], "list cmp (inverse same len)")
assert_neq([1, 2, 3], [], "list cmp (with empty)")
assert_neq([1, 2, 3], [1, 2], "list cmp (with prefix)")
assert_neq([1, 2, 3], [1, 2, 3, 4], "list cmp (as prefix)")

7
tests/basic_len.sol

@ -0,0 +1,7 @@
execfile("tests/_lib.sol")
assert_eq(0, #[], "empty list")
assert_eq(0, #{}, "empty map")
assert_eq(2, #[1, 2], "list len")
assert_eq(3, #{a=1, b=2, c=[3, 4, 5]}, "map len")

10
tests/basic_vars.sol

@ -0,0 +1,10 @@
execfile("tests/_lib.sol")
a = 3
assert_eq(a, a, "a (reflexive)")
assert_eq(3, a, "a")
assert_eq(a, 3, "a")
l = [0 9 3 1]
assert_eq(l, l, "l (reflexive)")
assert_eq([0, 9, 3, 1], l, "l")

5
tests/bt_range.sol

@ -0,0 +1,5 @@
execfile("tests/_lib.sol")
assert_eq([0, 1, 2, 3, 4], range(5), "range 5")
assert_eq(50, #range(50), "len range 50")
assert_eq(0, #range(0), "Empty range")

0
tests/fails.sol → tests/fails.sol.disabled

4
tests/lang_forex.sol

@ -0,0 +1,4 @@
execfile("tests/_lib.sol")
assert_eq(5, for i in range(10) do if i == 5 then break i end end, "break with value")
assert_eq([0, 2, 4, 6, 8], for i in range(5) do continue 2 * i end, "continue with value")

4
tests/lang_ifex.sol

@ -0,0 +1,4 @@
execfile("tests/_lib.sol")
assert_eq("true", if 1 then "true" else "false" end, "iftrue")
assert_eq("false", if 0 then "true" else "false" end, "iffalse")

47
tests/lang_scoping.sol

@ -0,0 +1,47 @@
execfile("tests/_lib.sol")
_G = debug.globals()
_G.a = 10
assert_eq(a, 10, "debug.globals write")
b = 7
assert_eq(_G.b, 7, "debug.globals read")
func()
assert_eq(a, 10, "scope inheritance (a)")
assert_eq(b, 7, "scope inheritance (b)")
_L = debug.locals()
_L.a = 17
assert_eq(a, 17, "debug.locals write")
b = 3
assert_eq(_L.b, 3, "debug.locals read")
assert_eq(#debug.scopes(), 2, "debug scope count")
end()
assert_eq(a, 10, "scope leak (direct)")
assert_eq(b, 7, "scope leak (direct)")
assert_eq(_G.a, 10, "scope leak (indirect)")
assert_eq(_G.b, 7, "scope leak (indirect)")
func()
_LG = debug.globals()
_LG.a = 13
_LG.b = 14
assert_eq(a, 13, "scope inheritance after outer write")
assert_eq(b, 14, "scope inheritance after outer write")
a = 9
b = 7
assert_eq(a, 9, "local write")
assert_eq(b, 7, "local write")
end()
assert_eq(a, 13, "outer write persistence")
assert_eq(b, 14, "outer write persistence")
-- FIXME: Attempting to repr these scopes causes an inf recursion
assert(debug.locals() == debug.globals(), "root scope locals == globals")
Загрузка…
Отмена
Сохранить