aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--compiler/compile.c8
-rw-r--r--tests/check.py36
3 files changed, 34 insertions, 12 deletions
diff --git a/Makefile b/Makefile
index 1056019..a75fab5 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,6 @@ $(BUILD)/aup: compiler/main.c compiler/compile.c compiler/aup.h | $(BUILD)
$(CC) $(CPPFLAGS) $(CFLAGS) compiler/main.c compiler/compile.c $(LDFLAGS) $(LDLIBS) -o $@
check: $(BUILD)/aup
- $(MAKE) -C $(PURE) build/pure-run build/pure-edit-module
+ $(MAKE) -C $(PURE) build/pure-run build/pure-edit-module build/pure-serve
$(MAKE) -C $(PURE)/prototypes/connections BUILD=build/tree STORE=store-tree.c build/tree/pure-edit
python3 tests/check.py --aup $(BUILD)/aup --pure-run $(PURE)/build/pure-run --pure $(PURE)
diff --git a/compiler/compile.c b/compiler/compile.c
index d2a3c74..e0d694e 100644
--- a/compiler/compile.c
+++ b/compiler/compile.c
@@ -2309,7 +2309,7 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) {
app_name(c, e, e->member);
const char *key = keyed_by(c, list);
if (!key) fail_at(c, e->line, e->column, "'%s' has no key; declare it as list of %s by a field to ask has",
- e->member, record_of(c, list)->name);
+ e->member, record_shown(c, c->module, record_of(c, list)));
if (e->count != 1) fail_at(c, e->line, e->column, "%s.has takes one key", e->member);
Type type = field_kind(record_of(c, list), (unsigned)c->list_key[list] - 1);
Kind given = wanted(c, e->args[0], handler, height, type);
@@ -2735,7 +2735,7 @@ static void statements(Compiler *c, const Stmt *s) {
fail_at(c, s->line, s->column, "a for does not add to the list it walks; add to another list, or after the for");
if (s->kind == S_INSERT && keyed_by(c, list))
fail_at(c, s->line, s->column, "'%s' is keyed by %s: %s.add(%s { ... }) places an item by its key", s->name,
- keyed_by(c, list), s->name, record_of(c, list)->name);
+ keyed_by(c, list), s->name, record_shown(c, c->module, record_of(c, list)));
if (s->kind == S_REMOVE) item_of(c, s->first, true, 0, list);
else index_of(c, s->first, true, 0, list);
if (s->kind == S_INSERT) record_fields(c, s->value, list, 1);
@@ -2759,7 +2759,7 @@ static void statements(Compiler *c, const Stmt *s) {
int list = list_state(c, s->name, s->line, s->column, NULL);
if (c->walking && list == c->walk_list)
fail_at(c, s->line, s->column, "a for does not add to the list it walks; add to another list, or after the for");
- const char *key = keyed_by(c, list), *record = record_of(c, list)->name;
+ const char *key = keyed_by(c, list), *record = record_shown(c, c->module, record_of(c, list));
if (s->kind == S_APPEND && key)
fail_at(c, s->line, s->column, "'%s' is keyed by %s: %s.add(%s { ... }) places an item by its key", s->name,
key, s->name, record);
@@ -2779,7 +2779,7 @@ static void statements(Compiler *c, const Stmt *s) {
int key = c->list_key[list] - 1;
for (unsigned k = 0; k < r->fields; ++k)
if (!values[k]) fail_at(c, s->value->line, s->value->column, "%s[...] = %s { ... } needs '%s'", s->name,
- r->name, r->field_names[k]);
+ record_shown(c, c->module, r), r->field_names[k]);
if (key >= 0) { /* require tasks[at].key == the value's key */
Expr *item = node(c, E_ITEM, s->line, s->column), *same = node(c, E_BINARY, s->line, s->column);
memcpy(item->name, s->name, NAME);
diff --git a/tests/check.py b/tests/check.py
index fdb7060..79da1ab 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -927,14 +927,17 @@ def libraries(aup, pure_run, work):
-def library_records(aup, pure_run, work):
+def library_records(aup, pure_run, pure, work):
"""A library declares records (docs/design/002, Pure's review 005): an
app writes them LIBRARY.NAME, and they are NAME in its module, so the
task list with its record moved into a library builds the same module,
byte for byte. Only the records the app's lists and rows hold go into
its module. A second module using the same library (a migration) reads
- and changes what the app stored, and the app reads it after. Names are
- one namespace in a module, however they were written."""
+ and changes what the app stored, and the app reads it after; a message's
+ rows of a library's record arrive over HTTP (pure-serve). Names are one
+ namespace in a module, however they were written; hints write a
+ library's record as the app does."""
+ import http.client
import re
import struct
lib = work / 'record-libs'
@@ -978,10 +981,26 @@ def library_records(aup, pure_run, work):
run(pure_run, lib / 'migrate.pmod', '--store', store, stdin='down bump\nup bump\n')
shown = json.loads(run(pure_run, lib / 'main.pmod', '--store', store, stdin='show\n').stdout.splitlines()[-1])
assert shown['views'] == {'count': 3, 'points': 30}, shown
- # A message's rows of a library's record.
+ # A message's rows of a library's record, posted as JSON to pure-serve.
assert build('rows', 'use store\ninput load = message {\n rows: list of store.User\n}\nstate n = 0\n'
- 'on load {\n n = n + load.rows.count\n}\nview loaded = n\n').returncode == 0, \
- build('rows', '').stderr
+ 'on load {\n n = n + sum(r.points for r in load.rows)\n}\nview loaded = n\n').returncode == 0
+ host = subprocess.Popen([str(pure / 'build' / 'pure-serve'), '--module', str(lib / 'rows.pmod'), '--store',
+ str(lib / 'rows-store'), '--port', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ try:
+ port = json.loads(host.stdout.readline())['port']
+ body = json.dumps({'rows': [{'name': 'a', 'points': 4}, {'name': 'b', 'points': 5}]}).encode()
+ connection = http.client.HTTPConnection('127.0.0.1', port, timeout=10)
+ connection.request('POST', '/inputs/load', body, {'Content-Type': 'application/json'})
+ answer = connection.getresponse()
+ assert answer.status == 200, (answer.status, answer.getheader('X-Pure-Error'), answer.read())
+ answer.read()
+ connection.request('GET', '/views/loaded')
+ loaded = connection.getresponse().read()
+ connection.close()
+ assert loaded == b'9', loaded
+ finally:
+ host.terminate()
+ host.communicate(timeout=30)
(lib / 'other.aup').write_text('record User { name: text }\n')
(lib / 'twofold.aup').write_text('record Item { n = 0 }\nfn Item = 1\n')
(lib / 'stateful.aup').write_text('record R { n = 0 }\nstate x = 0\n')
@@ -1001,6 +1020,9 @@ def library_records(aup, pure_run, work):
'main.aup:4:21', "'users' holds store.User, not User"),
('use store\nstate users: list of store.User\ninput j = text\non j { users.append(store.Nope { name: j }) }\n',
'main.aup:4:27', "library 'store' has no record 'Nope'"),
+ ('use store\nstate users: list of store.User by name\ninput j = text\n'
+ 'on j { users.append(store.User { name: j }) }\n', 'main.aup:4:8',
+ "'users' is keyed by name: users.add(store.User { ... }) places an item by its key"),
('use twofold\n', 'twofold.aup:2:4', "'Item' is already declared as a record"),
('use stateful\n', 'stateful.aup:2:1', 'a library declares only fn, record and use'),
]
@@ -1709,7 +1731,7 @@ def main():
report['keyed_snapshots'], report['keyed_failed_events'] = keyed(aup, pure_run, work, range(args.seeds))
report['stored_snapshots'], report['stored_restarts'] = stored_runs(aup, pure_run, work, range(args.text_seeds))
report['library_programs'], report['library_diagnostics'] = libraries(aup, pure_run, work)
- report['library_records'] = library_records(aup, pure_run, work)
+ report['library_records'] = library_records(aup, pure_run, args.pure.resolve(), work)
report['editor_parity_operations'], report['editor_parity_outcomes'] = \
editor_parity(aup, args.pure.resolve(), work, range(args.editor_seeds))
print(json.dumps(report))