mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
support starting and stopping tasks
This commit is contained in:
parent
d24319179c
commit
8bd9605b25
4 changed files with 136 additions and 33 deletions
|
@ -26,6 +26,34 @@ static void test_task_creation(void) {
|
||||||
tc_replica_free(rep);
|
tc_replica_free(rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// freeing a mutable task works, marking it immutable
|
||||||
|
static void test_task_free_mutable_task(void) {
|
||||||
|
TCReplica *rep = tc_replica_new_in_memory();
|
||||||
|
TEST_ASSERT_NULL(tc_replica_error(rep));
|
||||||
|
|
||||||
|
TCTask *task = tc_replica_new_task(
|
||||||
|
rep,
|
||||||
|
TC_STATUS_PENDING,
|
||||||
|
tc_string_borrow("my task"));
|
||||||
|
TEST_ASSERT_NOT_NULL(task);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task));
|
||||||
|
TCUuid uuid = tc_task_get_uuid(task);
|
||||||
|
|
||||||
|
tc_task_to_mut(task, rep);
|
||||||
|
TEST_ASSERT_TRUE(tc_task_set_status(task, TC_STATUS_DELETED));
|
||||||
|
TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task));
|
||||||
|
|
||||||
|
tc_task_free(task); // implicitly converts to immut
|
||||||
|
|
||||||
|
task = tc_replica_get_task(rep, uuid);
|
||||||
|
TEST_ASSERT_NOT_NULL(task);
|
||||||
|
TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task));
|
||||||
|
tc_task_free(task);
|
||||||
|
|
||||||
|
tc_replica_free(rep);
|
||||||
|
}
|
||||||
|
|
||||||
// updating status on a task works
|
// updating status on a task works
|
||||||
static void test_task_get_set_status(void) {
|
static void test_task_get_set_status(void) {
|
||||||
TCReplica *rep = tc_replica_new_in_memory();
|
TCReplica *rep = tc_replica_new_in_memory();
|
||||||
|
@ -83,11 +111,38 @@ static void test_task_get_set_description(void) {
|
||||||
tc_replica_free(rep);
|
tc_replica_free(rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// starting and stopping a task works, as seen by tc_task_is_active
|
||||||
|
static void test_task_start_stop_is_active(void) {
|
||||||
|
TCReplica *rep = tc_replica_new_in_memory();
|
||||||
|
TEST_ASSERT_NULL(tc_replica_error(rep));
|
||||||
|
|
||||||
|
TCTask *task = tc_replica_new_task(
|
||||||
|
rep,
|
||||||
|
TC_STATUS_PENDING,
|
||||||
|
tc_string_borrow("my task"));
|
||||||
|
TEST_ASSERT_NOT_NULL(task);
|
||||||
|
|
||||||
|
TEST_ASSERT_FALSE(tc_task_is_active(task));
|
||||||
|
|
||||||
|
tc_task_to_mut(task, rep);
|
||||||
|
|
||||||
|
TEST_ASSERT_FALSE(tc_task_is_active(task));
|
||||||
|
tc_task_start(task);
|
||||||
|
TEST_ASSERT_TRUE(tc_task_is_active(task));
|
||||||
|
tc_task_stop(task);
|
||||||
|
TEST_ASSERT_FALSE(tc_task_is_active(task));
|
||||||
|
|
||||||
|
tc_task_free(task);
|
||||||
|
tc_replica_free(rep);
|
||||||
|
}
|
||||||
|
|
||||||
int task_tests(void) {
|
int task_tests(void) {
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
// each test case above should be named here, in order.
|
// each test case above should be named here, in order.
|
||||||
RUN_TEST(test_task_creation);
|
RUN_TEST(test_task_creation);
|
||||||
|
RUN_TEST(test_task_free_mutable_task);
|
||||||
RUN_TEST(test_task_get_set_status);
|
RUN_TEST(test_task_get_set_status);
|
||||||
RUN_TEST(test_task_get_set_description);
|
RUN_TEST(test_task_get_set_description);
|
||||||
|
RUN_TEST(test_task_start_stop_is_active);
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,8 +251,8 @@ pub extern "C" fn tc_replica_error<'a>(rep: *mut TCReplica) -> *mut TCString<'st
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_replica_free(rep: *mut TCReplica) {
|
pub extern "C" fn tc_replica_free(rep: *mut TCReplica) {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - rep is not NULL
|
// - rep is not NULL (promised by caller)
|
||||||
// - caller will not use the TCReplica after this
|
// - caller will not use the TCReplica after this (promised by caller)
|
||||||
let replica = unsafe { TCReplica::from_arg(rep) };
|
let replica = unsafe { TCReplica::from_arg(rep) };
|
||||||
if replica.mut_borrowed {
|
if replica.mut_borrowed {
|
||||||
panic!("replica is borrowed and cannot be freed");
|
panic!("replica is borrowed and cannot be freed");
|
||||||
|
|
|
@ -10,6 +10,8 @@ use taskchampion::{Task, TaskMut};
|
||||||
///
|
///
|
||||||
/// A task carries no reference to the replica that created it, and can
|
/// A task carries no reference to the replica that created it, and can
|
||||||
/// be used until it is freed or converted to a TaskMut.
|
/// be used until it is freed or converted to a TaskMut.
|
||||||
|
///
|
||||||
|
/// All `tc_task_..` functions taking a task as an argument require that it not be NULL.
|
||||||
pub enum TCTask {
|
pub enum TCTask {
|
||||||
/// A regular, immutable task
|
/// A regular, immutable task
|
||||||
Immutable(Task),
|
Immutable(Task),
|
||||||
|
@ -202,7 +204,7 @@ pub extern "C" fn tc_task_get_status<'a>(task: *const TCTask) -> TCStatus {
|
||||||
wrap(task, |task| task.get_status().into())
|
wrap(task, |task| task.get_status().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: get_taskmap
|
// TODO: tc_task_get_taskmap (?? then we have to wrap a map..)
|
||||||
|
|
||||||
/// Get a task's description, or NULL if the task cannot be represented as a C string (e.g., if it
|
/// Get a task's description, or NULL if the task cannot be represented as a C string (e.g., if it
|
||||||
/// contains embedded NUL characters).
|
/// contains embedded NUL characters).
|
||||||
|
@ -214,19 +216,25 @@ pub extern "C" fn tc_task_get_description<'a>(task: *const TCTask) -> *mut TCStr
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: :get_entry
|
// TODO: tc_task_get_entry
|
||||||
// TODO: :get_wait
|
// TODO: tc_task_get_wait
|
||||||
// TODO: :get_modified
|
// TODO: tc_task_get_modified
|
||||||
// TODO: :is_waiting
|
// TODO: tc_task_is_waiting
|
||||||
// TODO: :is_active
|
|
||||||
// TODO: :has_tag
|
/// Check if a task is active (started and not stopped).
|
||||||
// TODO: :get_tags
|
#[no_mangle]
|
||||||
// TODO: :get_annotations
|
pub extern "C" fn tc_task_is_active<'a>(task: *const TCTask) -> bool {
|
||||||
// TODO: :get_uda
|
wrap(task, |task| task.is_active())
|
||||||
// TODO: :get_udas
|
}
|
||||||
// TODO: :get_legacy_uda
|
|
||||||
// TODO: :get_legacy_udas
|
// TODO: tc_task_has_tag
|
||||||
// TODO: :get_modified
|
// TODO: tc_task_get_tags
|
||||||
|
// TODO: tc_task_get_annotations
|
||||||
|
// TODO: tc_task_get_uda
|
||||||
|
// TODO: tc_task_get_udas
|
||||||
|
// TODO: tc_task_get_legacy_uda
|
||||||
|
// TODO: tc_task_get_legacy_udas
|
||||||
|
// TODO: tc_task_get_modified
|
||||||
|
|
||||||
/// Set a mutable task's status.
|
/// Set a mutable task's status.
|
||||||
///
|
///
|
||||||
|
@ -261,8 +269,29 @@ pub extern "C" fn tc_task_set_description<'a>(
|
||||||
// TODO: tc_task_set_entry
|
// TODO: tc_task_set_entry
|
||||||
// TODO: tc_task_set_wait
|
// TODO: tc_task_set_wait
|
||||||
// TODO: tc_task_set_modified
|
// TODO: tc_task_set_modified
|
||||||
// TODO: tc_task_start
|
|
||||||
// TODO: tc_task_stop
|
/// Start a task.
|
||||||
|
///
|
||||||
|
/// TODO: error
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn tc_task_start<'a>(task: *mut TCTask) {
|
||||||
|
wrap_mut(task, |task| {
|
||||||
|
task.start()?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop a task.
|
||||||
|
///
|
||||||
|
/// TODO: error
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn tc_task_stop<'a>(task: *mut TCTask) {
|
||||||
|
wrap_mut(task, |task| {
|
||||||
|
task.stop()?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: tc_task_done
|
// TODO: tc_task_done
|
||||||
// TODO: tc_task_delete
|
// TODO: tc_task_delete
|
||||||
// TODO: tc_task_add_tag
|
// TODO: tc_task_add_tag
|
||||||
|
@ -274,21 +303,19 @@ pub extern "C" fn tc_task_set_description<'a>(
|
||||||
// TODO: tc_task_set_legacy_uda
|
// TODO: tc_task_set_legacy_uda
|
||||||
// TODO: tc_task_remove_legacy_uda
|
// TODO: tc_task_remove_legacy_uda
|
||||||
|
|
||||||
/// Free a task. The given task must not be NULL and must be immutable. The task must not be used
|
/// Free a task. The given task must not be NULL. The task must not be used after this function
|
||||||
/// after this function returns, and must not be freed more than once.
|
/// returns, and must not be freed more than once.
|
||||||
///
|
///
|
||||||
/// The restriction that the task must be immutable may be lifted (TODO)
|
/// If the task is currently mutable, it will first be made immutable.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn tc_task_free<'a>(task: *mut TCTask) {
|
pub extern "C" fn tc_task_free<'a>(task: *mut TCTask) {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - rep is not NULL
|
// - rep is not NULL (promised by caller)
|
||||||
// - caller will not use the TCTask after this
|
// - caller will not use the TCTask after this (promised by caller)
|
||||||
let tctask = unsafe { TCTask::from_arg(task) };
|
let mut tctask = unsafe { TCTask::from_arg(task) };
|
||||||
if !matches!(tctask, TCTask::Immutable(_)) {
|
|
||||||
// this limit is in place because we require the caller to supply a pointer
|
// convert to immut if it was mutable
|
||||||
// to the replica to make a task immutable, and no such pointer is available
|
tctask.to_immut();
|
||||||
// here.
|
|
||||||
panic!("Task must be immutable when freed");
|
|
||||||
}
|
|
||||||
drop(tctask);
|
drop(tctask);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,8 @@ typedef struct TCString TCString;
|
||||||
*
|
*
|
||||||
* A task carries no reference to the replica that created it, and can
|
* A task carries no reference to the replica that created it, and can
|
||||||
* be used until it is freed or converted to a TaskMut.
|
* be used until it is freed or converted to a TaskMut.
|
||||||
|
*
|
||||||
|
* All `tc_task_..` functions taking a task as an argument require that it not be NULL.
|
||||||
*/
|
*/
|
||||||
typedef struct TCTask TCTask;
|
typedef struct TCTask TCTask;
|
||||||
|
|
||||||
|
@ -248,6 +250,11 @@ enum TCStatus tc_task_get_status(const struct TCTask *task);
|
||||||
*/
|
*/
|
||||||
struct TCString *tc_task_get_description(const struct TCTask *task);
|
struct TCString *tc_task_get_description(const struct TCTask *task);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a task is active (started and not stopped).
|
||||||
|
*/
|
||||||
|
bool tc_task_is_active(const struct TCTask *task);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a mutable task's status.
|
* Set a mutable task's status.
|
||||||
*
|
*
|
||||||
|
@ -263,10 +270,24 @@ bool tc_task_set_status(struct TCTask *task, enum TCStatus status);
|
||||||
bool tc_task_set_description(struct TCTask *task, struct TCString *description);
|
bool tc_task_set_description(struct TCTask *task, struct TCString *description);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a task. The given task must not be NULL and must be immutable. The task must not be used
|
* Start a task.
|
||||||
* after this function returns, and must not be freed more than once.
|
|
||||||
*
|
*
|
||||||
* The restriction that the task must be immutable may be lifted (TODO)
|
* TODO: error
|
||||||
|
*/
|
||||||
|
void tc_task_start(struct TCTask *task);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a task.
|
||||||
|
*
|
||||||
|
* TODO: error
|
||||||
|
*/
|
||||||
|
void tc_task_stop(struct TCTask *task);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a task. The given task must not be NULL. The task must not be used after this function
|
||||||
|
* returns, and must not be freed more than once.
|
||||||
|
*
|
||||||
|
* If the task is currently mutable, it will first be made immutable.
|
||||||
*/
|
*/
|
||||||
void tc_task_free(struct TCTask *task);
|
void tc_task_free(struct TCTask *task);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue