//////////////////////////////////////////////////////////////////////////////// // // Copyright 2022, Dustin J. Mitchell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // https://www.opensource.org/licenses/mit-license.php // //////////////////////////////////////////////////////////////////////////////// #include // cmake.h include header must come first #include #include #include #include #include #include std::string uuid2str(tc::Uuid uuid) { return static_cast(uuid.to_string()); } //////////////////////////////////////////////////////////////////////////////// // Tests for the basic cxxbridge functionality. This focuses on the methods with // complex cxxbridge implementations, rather than those with complex Rust // implementations but simple APIs, like sync. int main(int, char **) { UnitTest t; std::string str; auto replica = tc::new_replica_in_memory(); auto uuid = tc::uuid_v4(); auto uuid2 = tc::uuid_v4(); t.is(uuid2str(uuid).size(), (size_t)36, "uuid string is the right length"); rust::Vec ops; auto task = tc::create_task(uuid, ops); t.is(uuid2str(task->get_uuid()), uuid2str(uuid), "new task has correct uuid"); task->update("status", "pending", ops); task->update("description", "a task", ops); task->update("description", "a cool task", ops); tc::add_undo_point(ops); task->delete_task(ops); t.is(ops[0].is_create(), true, "ops[0] is create"); t.is(uuid2str(ops[0].get_uuid()), uuid2str(uuid), "ops[0] has correct uuid"); t.is(ops[1].is_update(), true, "ops[1] is update"); t.is(uuid2str(ops[1].get_uuid()), uuid2str(uuid), "ops[1] has correct uuid"); ops[1].get_property(str); t.is(str, "status", "ops[1] property is 'status'"); t.ok(ops[1].get_value(str), "get_value succeeds"); t.is(str, "pending", "ops[1] value is 'pending'"); t.ok(!ops[1].get_old_value(str), "get_old_value has no old value"); t.is(ops[2].is_update(), true, "ops[2] is update"); t.is(uuid2str(ops[2].get_uuid()), uuid2str(uuid), "ops[2] has correct uuid"); ops[2].get_property(str); t.is(str, "description", "ops[2] property is 'description'"); t.ok(ops[2].get_value(str), "get_value succeeds"); t.is(str, "a task", "ops[2] value is 'a task'"); t.ok(!ops[2].get_old_value(str), "get_old_value has no old value"); t.is(ops[3].is_update(), true, "ops[3] is update"); t.is(uuid2str(ops[3].get_uuid()), uuid2str(uuid), "ops[3] has correct uuid"); ops[3].get_property(str); t.is(str, "description", "ops[3] property is 'description'"); t.ok(ops[3].get_value(str), "get_value succeeds"); t.is(str, "a cool task", "ops[3] value is 'a cool task'"); t.ok(ops[3].get_old_value(str), "get_old_value succeeds"); t.is(str, "a task", "ops[3] old value is 'a task'"); t.is(ops[4].is_undo_point(), true, "ops[4] is undo_point"); t.is(ops[5].is_delete(), true, "ops[5] is delete"); t.is(uuid2str(ops[5].get_uuid()), uuid2str(uuid), "ops[5] has correct uuid"); auto old_task = ops[5].get_old_task(); // old_task is in arbitrary order, so just check that status is in there. bool found = false; for (auto &pv : old_task) { std::string p = static_cast(pv.prop); if (p == "status") { std::string v = static_cast(pv.value); t.is(v, "pending", "old_task has status:pending"); found = true; } } t.ok(found, "found the status property in ops[5].old_task"); replica->commit_operations(std::move(ops)); auto maybe_task2 = replica->get_task_data(tc::uuid_v4()); t.ok(maybe_task2.is_none(), "looking up a random uuid gets nothing"); // The last operation deleted the task, but we want to see the task, so undo it.. auto undo_ops = replica->get_undo_operations(); t.ok(replica->commit_reversed_operations(std::move(undo_ops)), "undo committed successfully"); auto maybe_task3 = replica->get_task_data(uuid); t.ok(maybe_task3.is_some(), "looking up the original uuid get TaskData"); rust::Box task3 = maybe_task3.take(); t.is(uuid2str(task3->get_uuid()), uuid2str(uuid), "reloaded task has correct uuid"); t.ok(task3->get("description", str), "reloaded task has a description"); t.is(str, "a cool task", "reloaded task has correct description"); t.ok(task3->get("status", str), "reloaded task has a status"); t.is(str, "pending", "reloaded task has correct status"); t.is(task3->properties().size(), (size_t)2, "task has 2 properties"); t.is(task3->items().size(), (size_t)2, "task has 2 items"); rust::Vec ops2; auto task4 = tc::create_task(uuid2, ops2); task4->update("description", "another", ops2); replica->commit_operations(std::move(ops2)); auto all_tasks = replica->all_task_data(); t.is(all_tasks.size(), (size_t)2, "now there are 2 tasks"); for (auto &maybe_task : all_tasks) { t.ok(maybe_task.is_some(), "all_tasks is fully populated"); auto task = maybe_task.take(); if (task->get_uuid() == uuid) { t.ok(task->get("description", str), "get_value succeeds"); t.is(str, "a cool task", "description is 'a cool task'"); } } // Check exception formatting. try { replica->sync_to_local("/does/not/exist", false); // tc::new_replica_on_disk("/does/not/exist", false); } catch (rust::Error &err) { t.is(err.what(), "unable to open database file: /does/not/exist/taskchampion-local-sync-server.sqlite3: " "Error code 14: Unable to open the database file", "error message has full context"); } return 0; } ////////////////////////////////////////////////////////////////////////////////