branch: externals/triples commit aca95ba7f34af4395eae05e4a62b48da3207bd59 Author: Andrew Hyatt <ahy...@gmail.com> Commit: Andrew Hyatt <ahy...@gmail.com>
Ensure that we don't duplicate triples. Also, remove flakiness in tests caused by changing plist and list ordering. --- triples-test.el | 72 ++++++++++++++++++++++++++++++++++++--------------------- triples.el | 12 ++++++---- 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/triples-test.el b/triples-test.el index f539727cd8..e3172fa42b 100644 --- a/triples-test.el +++ b/triples-test.el @@ -1,6 +1,7 @@ ;;; triples-test.el --- Tests for triples module. (require 'triples) (require 'seq) +(require 'kv) ;;; Code: @@ -46,6 +47,10 @@ easily debug into it.") (named schema/property alternate-names) (named schema/property nicknames)))))) +(defun triples-test-list-sort (list) + "Standard sort for LIST for test stability." + (sort list (lambda (a b) (string< (format "%s" a) (format "%s" b))) )) + (ert-deftest triples-schema-crud () (triples-test-with-temp-db (triples-add-schema db 'named @@ -53,8 +58,8 @@ easily debug into it.") (should (equal '(:base/unique t) (triples-properties-for-predicate db 'named/name))) (should (equal - '(name alternate-names) - (triples-predicates-for-type db 'named))))) + (triples-test-list-sort '(name alternate-names)) + (triples-test-list-sort (triples-predicates-for-type db 'named)))))) (ert-deftest triples-properties-for-predicate () (triples-test-with-temp-db @@ -88,14 +93,20 @@ easily debug into it.") (should-error (triples-verify-schema-compliant db '(("foo" named/name "bar" (:index 0))))) (should (triples-verify-schema-compliant db '(("foo" named/alternate-names "bar" (:index 0))))))) +(defun triples-test-plist-sort (plist) + "Sort PLIST in a standard way, for comparison." + (kvalist->plist + (kvalist-sort (kvplist->alist plist) + (lambda (a b) (string< (format "%s" a) (format "%s" b)))))) + (ert-deftest triples-crud () (triples-test-with-temp-db (triples-add-schema db 'named '(name :unique t) 'alias) (triples-set-type db "foo" 'named :name "Name" :alias '("alias1" "alias2")) - (should (equal '(:name "Name" :alias ("alias1" "alias2")) - (triples-get-type db "foo" 'named))) + (should (equal (triples-test-plist-sort '(:name "Name" :alias ("alias1" "alias2"))) + (triples-test-plist-sort (triples-get-type db "foo" 'named)))) (should (equal (triples-get-types db "foo") '(named))) (should-not (triples-get-type db "bar" 'named)) (should-not (triples-get-types db "bar")) @@ -138,16 +149,6 @@ easily debug into it.") (triples-get-type db "en/US" 'locale))) (should-error (triples-set-type db "en/US" 'locale :used-in-name '("bar"))))) -(ert-deftest triples-with-preciate-object () - (triples-test-with-temp-db - (triples-add-schema db 'named '(name)) - (should-not (triples-search db 'named/name "Fred")) - (triples-set-type db "foo" 'named :name "My Name Is Fred Foo") - (triples-set-type db "bar" 'named :name "My Name Is Betty Bar") - (should (equal - '(("foo" named/name "My Name Is Fred Foo" nil)) - (triples-search db 'named/name "Fred"))))) - (ert-deftest triples-with-predicate () (triples-test-with-temp-db (triples-add-schema db 'named '(name)) @@ -163,12 +164,27 @@ easily debug into it.") (ert-deftest triples-subjects-of-type () (triples-test-with-temp-db - (triples-add-schema db 'named '(name)) - (should-not (triples-subjects-of-type db 'named)) - (triples-set-type db "foo" 'named :name "My Name Is Fred Foo") - (triples-set-type db "bar" 'named :name "My Name Is Betty Bar") - (should (seq-set-equal-p '("foo" "bar") - (triples-subjects-of-type db 'named))))) + (triples-add-schema db 'named '(name)) + (should-not (triples-subjects-of-type db 'named)) + (triples-set-type db "foo" 'named :name "My Name Is Fred Foo") + (triples-set-type db "bar" 'named :name "My Name Is Betty Bar") + (should (seq-set-equal-p '("foo" "bar") + (triples-subjects-of-type db 'named))))) + +(ert-deftest triples-no-dups () + (triples-test-with-temp-db + ;; Just add a marker schema, no attributes + (triples-add-schema db 'marker) + (triples-set-type db "foo" 'marker) + (should (equal '((1)) + (emacsql db [:select (funcall count) :from triples :where (= subject $s1) + :and (= predicate 'base/type) :and (= object 'marker)] + "foo"))) + (triples-set-type db "foo" 'marker) + (should (equal '((1)) + (emacsql db [:select (funcall count) :from triples :where (= subject $s1) + :and (= predicate 'base/type) :and (= object 'marker)] + "foo"))))) (ert-deftest triples-readme () (triples-test-with-temp-db @@ -185,14 +201,18 @@ easily debug into it.") (triples-delete-subject db "alice") (triples-set-type db "alice" 'person :name "Alice Aardvark" :age 41) (triples-set-type db "alice" 'employee :id 1901 :manager "bob") - (should (equal (triples-get-subject db "alice") - '(:person/name "Alice Aardvark" :person/age 41 :employee/id 1901 - :employee/manager "bob" :employee/reportees ("catherine" "dennis")))) + (should (equal (triples-test-plist-sort (triples-get-subject db "alice")) + (triples-test-plist-sort '(:person/name "Alice Aardvark" :person/age 41 + :employee/id 1901 + :employee/manager "bob" + :employee/reportees ("catherine" "dennis"))))) (triples-set-subject db "alice" '(person :name "Alice Aardvark" :age 41) '(employee :id 1901 :manager "bob")) - (should (equal (triples-get-subject db "alice") - '(:person/name "Alice Aardvark" :person/age 41 :employee/id 1901 - :employee/manager "bob" :employee/reportees ("catherine" "dennis")))))) + (should (equal (triples-test-plist-sort (triples-get-subject db "alice")) + (triples-test-plist-sort '(:person/name "Alice Aardvark" :person/age 41 + :employee/id 1901 + :employee/manager "bob" + :employee/reportees ("catherine" "dennis"))))))) (provide 'triples-test) diff --git a/triples.el b/triples.el index 66d2c994b7..a26c29a874 100644 --- a/triples.el +++ b/triples.el @@ -44,14 +44,18 @@ (properties)])]) (emacsql db [:create-index subject_idx :on triples [subject]]) (emacsql db [:create-index subject_predicate_idx :on triples [subject predicate]]) - (emacsql db [:create-index predicate_object_idx :on triples [predicate object]])) + (emacsql db [:create-index predicate_object_idx :on triples [predicate object]]) + (emacsql db [:create-unique-index subject_predicate_object_properties_idx :on triples [subject predicate object properties]])) db)) (defun triples--ensure-property-val (vec) - "Return a VEC has 4 elements." + "Return a VEC has 4 elements. +We add a bogus value as a property because we want to be able +to enforce unique constraints, which sqlite will not do will NULL +values." (if (= (length vec) 4) vec - (vconcat vec '(nil)))) + (vconcat vec '((:empty t))))) (defun triples--subjects (triples) "Return all unique subjects in TRIPLES." @@ -96,7 +100,7 @@ (mapcar #'cl-second (cdr sub-triples))))))) (triples--group-by-subjects (cdr op))))) (mapc (lambda (triple) - (emacsql db [:insert :into triples + (emacsql db [:replace :into triples :values $v1] (triples--ensure-property-val (apply #'vector triple)))) (cdr op)))