taskwarrior/test/tc_cpp_test.cpp
2024-08-12 00:20:17 +00:00

157 lines
6.6 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// 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>
// cmake.h include header must come first
#include <rust/cxx.h>
#include <stdlib.h>
#include <taskchampion-cpp/lib.h>
#include <test.h>
#include <unistd.h>
#include <iostream>
std::string uuid2str(tc::Uuid uuid) { return static_cast<std::string>(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 TEST_NAME(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<tc::Operation> 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<std::string>(pv.prop);
if (p == "status") {
std::string v = static_cast<std::string>(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<tc::TaskData> 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<tc::Operation> 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;
}
////////////////////////////////////////////////////////////////////////////////