Introduce GcalRange
GcalRange is a new structure that represents a time range. It is always inclusive at the start, and exclusive at the end.
This commit is contained in:
@@ -0,0 +1,492 @@
|
||||
/* gcal-range.c
|
||||
*
|
||||
* Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "gcal-range.h"
|
||||
|
||||
#include "gcal-date-time-utils.h"
|
||||
|
||||
struct _GcalRange
|
||||
{
|
||||
gatomicrefcount ref_count;
|
||||
|
||||
GDateTime *range_start;
|
||||
GDateTime *range_end;
|
||||
GcalRangeType range_type;
|
||||
};
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GcalRange, gcal_range, gcal_range_ref, gcal_range_unref)
|
||||
|
||||
typedef int (*CompareDateTimeFunc) (GDateTime *a,
|
||||
GDateTime *b);
|
||||
|
||||
static CompareDateTimeFunc
|
||||
get_compare_func (GcalRange *a,
|
||||
GcalRange *b)
|
||||
{
|
||||
if (a->range_type == GCAL_RANGE_DATE_ONLY || b->range_type == GCAL_RANGE_DATE_ONLY)
|
||||
return gcal_date_time_compare_date;
|
||||
else
|
||||
return (CompareDateTimeFunc) g_date_time_compare;
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_copy:
|
||||
* @self: a #GcalRange
|
||||
*
|
||||
* Makes a deep copy of a #GcalRange.
|
||||
*
|
||||
* Returns: (transfer full): A newly created #GcalRange with the same
|
||||
* contents as @self
|
||||
*/
|
||||
GcalRange *
|
||||
gcal_range_copy (GcalRange *self)
|
||||
{
|
||||
GcalRange *copy;
|
||||
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (!g_atomic_ref_count_compare (&self->ref_count, 0), NULL);
|
||||
|
||||
copy = gcal_range_new (self->range_start, self->range_end, self->range_type);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_range_free (GcalRange *self)
|
||||
{
|
||||
g_assert (self);
|
||||
g_assert (g_atomic_ref_count_compare (&self->ref_count, 0));
|
||||
|
||||
gcal_clear_date_time (&self->range_start);
|
||||
gcal_clear_date_time (&self->range_end);
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_ref:
|
||||
* @self: A #GcalRange
|
||||
*
|
||||
* Increments the reference count of @self by one.
|
||||
*
|
||||
* Returns: (transfer full): @self
|
||||
*/
|
||||
GcalRange *
|
||||
gcal_range_ref (GcalRange *self)
|
||||
{
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (!g_atomic_ref_count_compare (&self->ref_count, 0), NULL);
|
||||
|
||||
g_atomic_ref_count_inc (&self->ref_count);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_unref:
|
||||
* @self: A #GcalRange
|
||||
*
|
||||
* Decrements the reference count of @self by one, freeing the structure when
|
||||
* the reference count reaches zero.
|
||||
*/
|
||||
void
|
||||
gcal_range_unref (GcalRange *self)
|
||||
{
|
||||
g_return_if_fail (self);
|
||||
g_return_if_fail (!g_atomic_ref_count_compare (&self->ref_count, 0));
|
||||
|
||||
if (g_atomic_ref_count_dec (&self->ref_count))
|
||||
gcal_range_free (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_new:
|
||||
*
|
||||
* Creates a new #GcalRange.
|
||||
*
|
||||
* Returns: (transfer full): A newly created #GcalRange
|
||||
*/
|
||||
GcalRange*
|
||||
gcal_range_new (GDateTime *range_start,
|
||||
GDateTime *range_end,
|
||||
GcalRangeType range_type)
|
||||
{
|
||||
g_return_val_if_fail (range_start, NULL);
|
||||
g_return_val_if_fail (range_end, NULL);
|
||||
g_return_val_if_fail (g_date_time_compare (range_start, range_end) <= 0, NULL);
|
||||
|
||||
return gcal_range_new_take (g_date_time_ref (range_start),
|
||||
g_date_time_ref (range_end),
|
||||
range_type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gcal_range_new_take:
|
||||
* @range_start: (transfer full): a #GDateTime
|
||||
* @range_end: (transfer full): a #GDateTime
|
||||
*
|
||||
* Creates a new #GcalRange and takes ownership of
|
||||
* @range_start and @range_end.
|
||||
*
|
||||
* Returns: (transfer full): A newly created #GcalRange
|
||||
*/
|
||||
GcalRange*
|
||||
gcal_range_new_take (GDateTime *range_start,
|
||||
GDateTime *range_end,
|
||||
GcalRangeType range_type)
|
||||
{
|
||||
GcalRange *self;
|
||||
|
||||
g_return_val_if_fail (range_start, NULL);
|
||||
g_return_val_if_fail (range_end, NULL);
|
||||
g_return_val_if_fail (g_date_time_compare (range_start, range_end) <= 0, NULL);
|
||||
|
||||
self = g_new0 (GcalRange, 1);
|
||||
g_atomic_ref_count_init (&self->ref_count);
|
||||
|
||||
self->range_start = range_start;
|
||||
self->range_end = range_end;
|
||||
self->range_type = range_type;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_get_start:
|
||||
* @self: a #GcalRange
|
||||
*
|
||||
* Retrieves a copy of @self's range start.
|
||||
*
|
||||
* Returns: (transfer full): a #GDateTime
|
||||
*/
|
||||
GDateTime*
|
||||
gcal_range_get_start (GcalRange *self)
|
||||
{
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (!g_atomic_ref_count_compare (&self->ref_count, 0), NULL);
|
||||
|
||||
return g_date_time_ref (self->range_start);
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_get_end:
|
||||
* @self: a #GcalRange
|
||||
*
|
||||
* Retrieves a copy of @self's range end.
|
||||
*
|
||||
* Returns: (transfer full): a #GDateTime
|
||||
*/
|
||||
GDateTime*
|
||||
gcal_range_get_end (GcalRange *self)
|
||||
{
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (!g_atomic_ref_count_compare (&self->ref_count, 0), NULL);
|
||||
|
||||
return g_date_time_ref (self->range_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_calculate_overlap:
|
||||
* @a: a #GcalRange
|
||||
* @b: a #GcalRange
|
||||
* @out_position: (direction out)(nullable): return location for a #GcalRangePosition
|
||||
*
|
||||
* Calculates how @a and @b overlap.
|
||||
*
|
||||
* The position returned at @out_position is always relative to @a. For example,
|
||||
* %GCAL_RANGE_AFTER means @a is after @b. The heuristic for the position is:
|
||||
*
|
||||
* 1. If @a begins before @b, @a comes before @b.
|
||||
* 2. If @a and @b begin at precisely the same moment, but @a ends before @b,
|
||||
* @a comes before @b.
|
||||
* 3. Otherwise, @b comes before @a.
|
||||
*
|
||||
* Returns: the overlap result between @a and @b
|
||||
*/
|
||||
GcalRangeOverlap
|
||||
gcal_range_calculate_overlap (GcalRange *a,
|
||||
GcalRange *b,
|
||||
GcalRangePosition *out_position)
|
||||
{
|
||||
CompareDateTimeFunc compare_func;
|
||||
GcalRangePosition position;
|
||||
GcalRangeOverlap overlap;
|
||||
gint a_start_b_start_diff;
|
||||
gint a_end_b_end_diff;
|
||||
|
||||
g_return_val_if_fail (a && b, GCAL_RANGE_NO_OVERLAP);
|
||||
|
||||
/*
|
||||
* There are 11 cases that we need to take care of:
|
||||
*
|
||||
* 1. Equal
|
||||
*
|
||||
* A |------------------------|
|
||||
* B |------------------------|
|
||||
*
|
||||
* 2. Superset
|
||||
*
|
||||
* i.
|
||||
* A |------------------------|
|
||||
* B |-------------------|
|
||||
*
|
||||
* ii.
|
||||
* A |------------------------|
|
||||
* B |-------------------|
|
||||
*
|
||||
* iii.
|
||||
* A |------------------------|
|
||||
* B |-------------------|
|
||||
*
|
||||
* 3. Subset
|
||||
*
|
||||
* i.
|
||||
* A |-------------------|
|
||||
* B |------------------------|
|
||||
*
|
||||
* ii.
|
||||
* A |-------------------|
|
||||
* B |------------------------|
|
||||
*
|
||||
* iii.
|
||||
* A |-------------------|
|
||||
* B |------------------------|
|
||||
*
|
||||
* 4. Intersection
|
||||
*
|
||||
* i.
|
||||
* A |--------------------|
|
||||
* B |--------------------|
|
||||
*
|
||||
* ii.
|
||||
* A |--------------------|
|
||||
* B |--------------------|
|
||||
*
|
||||
* 5) No Overlap
|
||||
*
|
||||
* i.
|
||||
* A |------------|
|
||||
* B |-----------|
|
||||
*
|
||||
* ii.
|
||||
* A |------------|
|
||||
* B |-----------|
|
||||
*
|
||||
*/
|
||||
|
||||
compare_func = get_compare_func (a, b);
|
||||
a_start_b_start_diff = compare_func (a->range_start, b->range_start);
|
||||
a_end_b_end_diff = compare_func (a->range_end, b->range_end);
|
||||
|
||||
if (a_start_b_start_diff == 0 && a_end_b_end_diff == 0)
|
||||
{
|
||||
/* Case 1, the easiest */
|
||||
overlap = GCAL_RANGE_EQUAL;
|
||||
position = GCAL_RANGE_MATCH;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a_start_b_start_diff == 0)
|
||||
{
|
||||
if (a_end_b_end_diff > 0)
|
||||
{
|
||||
/* Case 2.i */
|
||||
overlap = GCAL_RANGE_SUPERSET;
|
||||
position = GCAL_RANGE_AFTER;
|
||||
}
|
||||
else /* a_end_b_end_diff < 0 */
|
||||
{
|
||||
/* Case 3.i */
|
||||
overlap = GCAL_RANGE_SUBSET;
|
||||
position = GCAL_RANGE_BEFORE;
|
||||
}
|
||||
}
|
||||
else if (a_end_b_end_diff == 0)
|
||||
{
|
||||
if (a_start_b_start_diff < 0)
|
||||
{
|
||||
/* Case 2.iii */
|
||||
overlap = GCAL_RANGE_SUPERSET;
|
||||
position = GCAL_RANGE_BEFORE;
|
||||
}
|
||||
else /* a_start_b_start_diff > 0 */
|
||||
{
|
||||
/* Case 3.iii */
|
||||
overlap = GCAL_RANGE_SUBSET;
|
||||
position = GCAL_RANGE_AFTER;
|
||||
}
|
||||
}
|
||||
else /* a_start_b_start_diff != 0 && a_end_b_end_diff != 0 */
|
||||
{
|
||||
if (a_start_b_start_diff < 0 && a_end_b_end_diff > 0)
|
||||
{
|
||||
/* Case 2.ii */
|
||||
overlap = GCAL_RANGE_SUPERSET;
|
||||
position = GCAL_RANGE_BEFORE;
|
||||
}
|
||||
else if (a_start_b_start_diff > 0 && a_end_b_end_diff < 0)
|
||||
{
|
||||
/* Case 3.ii */
|
||||
overlap = GCAL_RANGE_SUBSET;
|
||||
position = GCAL_RANGE_AFTER;
|
||||
}
|
||||
else
|
||||
{
|
||||
gint a_start_b_end_diff;
|
||||
gint a_end_b_start_diff;
|
||||
|
||||
a_start_b_end_diff = compare_func (a->range_start, b->range_end);
|
||||
a_end_b_start_diff = compare_func (a->range_end, b->range_start);
|
||||
|
||||
/* No overlap cases */
|
||||
if (a_start_b_end_diff >= 0)
|
||||
{
|
||||
/* Case 5.i */
|
||||
overlap = GCAL_RANGE_NO_OVERLAP;
|
||||
position = GCAL_RANGE_AFTER;
|
||||
}
|
||||
else if (a_end_b_start_diff <= 0)
|
||||
{
|
||||
/* Case 5.ii */
|
||||
overlap = GCAL_RANGE_NO_OVERLAP;
|
||||
position = GCAL_RANGE_BEFORE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Intersection cases */
|
||||
if (a_start_b_start_diff < 0 && a_end_b_start_diff > 0 && a_end_b_end_diff < 0)
|
||||
{
|
||||
/* Case 4.i */
|
||||
overlap = GCAL_RANGE_INTERSECTS;
|
||||
position = GCAL_RANGE_BEFORE;
|
||||
}
|
||||
else if (a_start_b_start_diff > 0 && a_start_b_end_diff < 0 && a_end_b_end_diff > 0)
|
||||
{
|
||||
/* Case 4.ii */
|
||||
overlap = GCAL_RANGE_INTERSECTS;
|
||||
position = GCAL_RANGE_AFTER;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (out_position)
|
||||
*out_position = position;
|
||||
|
||||
return overlap;
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_compare:
|
||||
* @a: a #GcalRange
|
||||
* @b: a #GcalRange
|
||||
*
|
||||
* Compares @a and @b. See gcal_range_calculate_overlap() for the
|
||||
* rules of when a range comes before or after.
|
||||
*
|
||||
* Returns: -1 is @a comes before @b, 0 if they're equal, or 1 if
|
||||
* @a comes after @b.
|
||||
*/
|
||||
gint
|
||||
gcal_range_compare (GcalRange *a,
|
||||
GcalRange *b)
|
||||
{
|
||||
CompareDateTimeFunc compare_func;
|
||||
gint result;
|
||||
|
||||
g_return_val_if_fail (a && b, 0);
|
||||
|
||||
compare_func = get_compare_func (a, b);
|
||||
result = compare_func (a->range_start, b->range_start);
|
||||
|
||||
if (result == 0)
|
||||
result = compare_func (b->range_end, a->range_end);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_union:
|
||||
* @a: a #GcalRange
|
||||
* @b: a #GcalRange
|
||||
*
|
||||
* Creates a new #GcalRange with the union of @a and @b.
|
||||
*
|
||||
* Returns: (transfer full): a #GcalRange.
|
||||
*/
|
||||
GcalRange*
|
||||
gcal_range_union (GcalRange *a,
|
||||
GcalRange *b)
|
||||
{
|
||||
CompareDateTimeFunc compare_func;
|
||||
GcalRangeType range_type;
|
||||
GDateTime *start;
|
||||
GDateTime *end;
|
||||
|
||||
g_return_val_if_fail (a != NULL, NULL);
|
||||
g_return_val_if_fail (b != NULL, NULL);
|
||||
|
||||
compare_func = get_compare_func (a, b);
|
||||
|
||||
if (compare_func (a->range_start, b->range_start) < 0)
|
||||
start = a->range_start;
|
||||
else
|
||||
start = b->range_start;
|
||||
|
||||
if (compare_func (a->range_end, b->range_end) > 0)
|
||||
end = a->range_end;
|
||||
else
|
||||
end = b->range_end;
|
||||
|
||||
if (a->range_type == GCAL_RANGE_DATE_ONLY || b->range_type == GCAL_RANGE_DATE_ONLY)
|
||||
range_type = GCAL_RANGE_DATE_ONLY;
|
||||
else
|
||||
range_type = GCAL_RANGE_DEFAULT;
|
||||
|
||||
return gcal_range_new (start, end, range_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_range_to_string:
|
||||
* @self: a #GcalRange
|
||||
*
|
||||
* Formats @self using ISO8601 dates. This is only useful
|
||||
* for debugging purposes.
|
||||
*
|
||||
* Returns: (transfer full): a string representation of @self
|
||||
*/
|
||||
gchar*
|
||||
gcal_range_to_string (GcalRange *self)
|
||||
{
|
||||
g_autofree gchar *start_string = NULL;
|
||||
g_autofree gchar *end_string = NULL;
|
||||
|
||||
g_return_val_if_fail (self, NULL);
|
||||
|
||||
start_string = g_date_time_format_iso8601 (self->range_start);
|
||||
end_string = g_date_time_format_iso8601 (self->range_end);
|
||||
|
||||
return g_strdup_printf ("[%s | %s)", start_string, end_string);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/* gcal-range.h
|
||||
*
|
||||
* Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GCAL_TYPE_RANGE (gcal_range_get_type ())
|
||||
typedef struct _GcalRange GcalRange;
|
||||
|
||||
/**
|
||||
* GcalRangeOverlap:
|
||||
*
|
||||
* @GCAL_RANGE_NO_OVERLAP: the ranges don't overlap
|
||||
* @GCAL_RANGE_INTERSECTS: the ranges intersect, but have non-intersected areas
|
||||
* @GCAL_RANGE_SUBSET: the first range is a subset of the second range
|
||||
* @GCAL_RANGE_EQUAL: the ranges are exactly equal
|
||||
* @GCAL_RANGE_SUPERSET: the first range is a superset of the second range
|
||||
*
|
||||
* The possible results of comparing two ranges.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GCAL_RANGE_NO_OVERLAP,
|
||||
GCAL_RANGE_INTERSECTS,
|
||||
GCAL_RANGE_SUBSET,
|
||||
GCAL_RANGE_EQUAL,
|
||||
GCAL_RANGE_SUPERSET,
|
||||
} GcalRangeOverlap;
|
||||
|
||||
/**
|
||||
* GcalRangePosition:
|
||||
*
|
||||
* @GCAL_RANGE_BEFORE: range @a is before @b
|
||||
* @GCAL_RANGE_AFTER: range @a is after @b
|
||||
*
|
||||
* The position of @a relative to @b. When the ranges are exactly equal, it
|
||||
* is undefined.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GCAL_RANGE_BEFORE = -1,
|
||||
GCAL_RANGE_MATCH = 0,
|
||||
GCAL_RANGE_AFTER = 1,
|
||||
} GcalRangePosition;
|
||||
|
||||
/**
|
||||
* GcalRangeType:
|
||||
*
|
||||
* @GCAL_RANGE_DEFAULT: the default (date and time) range type
|
||||
* @GCAL_RANGE_DATE_ONLY: date-only range
|
||||
*
|
||||
* The type of the range. When comparing ranges, if either one of
|
||||
* them is date-only, times are not considered by the comparison.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GCAL_RANGE_DEFAULT,
|
||||
GCAL_RANGE_DATE_ONLY,
|
||||
} GcalRangeType;
|
||||
|
||||
GType gcal_range_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GcalRange* gcal_range_new (GDateTime *range_start,
|
||||
GDateTime *range_end,
|
||||
GcalRangeType range_type);
|
||||
GcalRange* gcal_range_new_take (GDateTime *range_start,
|
||||
GDateTime *range_end,
|
||||
GcalRangeType range_type);
|
||||
GcalRange* gcal_range_copy (GcalRange *self);
|
||||
GcalRange* gcal_range_ref (GcalRange *self);
|
||||
void gcal_range_unref (GcalRange *self);
|
||||
|
||||
GDateTime* gcal_range_get_start (GcalRange *self);
|
||||
GDateTime* gcal_range_get_end (GcalRange *self);
|
||||
GcalRangeType gcal_range_get_range_type (GcalRange *self);
|
||||
|
||||
GcalRangeOverlap gcal_range_calculate_overlap (GcalRange *a,
|
||||
GcalRange *b,
|
||||
GcalRangePosition *out_position);
|
||||
|
||||
gint gcal_range_compare (GcalRange *a,
|
||||
GcalRange *b);
|
||||
|
||||
GcalRange* gcal_range_union (GcalRange *a,
|
||||
GcalRange *b);
|
||||
|
||||
gchar* gcal_range_to_string (GcalRange *self);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GcalRange, gcal_range_unref)
|
||||
|
||||
G_END_DECLS
|
||||
@@ -96,6 +96,7 @@ sources = files(
|
||||
'core/gcal-log.c',
|
||||
'core/gcal-manager.c',
|
||||
'core/gcal-night-light-monitor.c',
|
||||
'core/gcal-range.c',
|
||||
'core/gcal-recurrence.c',
|
||||
'core/gcal-shell-search-provider.c',
|
||||
'core/gcal-timeline.c',
|
||||
|
||||
@@ -35,6 +35,7 @@ tests = [
|
||||
'server',
|
||||
'discoverer',
|
||||
'event',
|
||||
'range',
|
||||
'range-tree',
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/* test-range.c
|
||||
*
|
||||
* Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "gcal-range.h"
|
||||
|
||||
/*********************************************************************************************************************/
|
||||
|
||||
static void
|
||||
range_new (void)
|
||||
{
|
||||
g_autoptr (GcalRange) range = NULL;
|
||||
g_autoptr (GDateTime) start = NULL;
|
||||
g_autoptr (GDateTime) end = NULL;
|
||||
|
||||
start = g_date_time_new_now_utc ();
|
||||
end = g_date_time_add_seconds (start, 1);
|
||||
|
||||
range = gcal_range_new (start, end, GCAL_RANGE_DEFAULT);
|
||||
g_assert_nonnull (range);
|
||||
}
|
||||
|
||||
/*********************************************************************************************************************/
|
||||
|
||||
static void
|
||||
range_invalid (void)
|
||||
{
|
||||
|
||||
g_autoptr (GcalRange) range = NULL;
|
||||
g_autoptr (GDateTime) start = NULL;
|
||||
g_autoptr (GDateTime) end = NULL;
|
||||
|
||||
start = g_date_time_new_now_utc ();
|
||||
end = g_date_time_add_seconds (start, 1);
|
||||
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "gcal_range_new: assertion 'range_start' failed");
|
||||
range = gcal_range_new (NULL, NULL, GCAL_RANGE_DEFAULT);
|
||||
g_test_assert_expected_messages ();
|
||||
g_assert_null (range);
|
||||
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "gcal_range_new: assertion 'range_start' failed");
|
||||
range = gcal_range_new (NULL, end, GCAL_RANGE_DEFAULT);
|
||||
g_test_assert_expected_messages ();
|
||||
g_assert_null (range);
|
||||
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "gcal_range_new: assertion 'range_end' failed");
|
||||
range = gcal_range_new (start, NULL, GCAL_RANGE_DEFAULT);
|
||||
g_test_assert_expected_messages ();
|
||||
g_assert_null (range);
|
||||
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "gcal_range_new: assertion 'g_date_time_compare (range_start, range_end) <= 0' failed");
|
||||
range = gcal_range_new (end, start, GCAL_RANGE_DEFAULT);
|
||||
g_test_assert_expected_messages ();
|
||||
g_assert_null (range);
|
||||
}
|
||||
|
||||
/*********************************************************************************************************************/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *start;
|
||||
const gchar *end;
|
||||
} TextRange;
|
||||
|
||||
static struct {
|
||||
TextRange first;
|
||||
TextRange second;
|
||||
GcalRangeOverlap expected_overlap;
|
||||
GcalRangePosition expected_position;
|
||||
} ranges[] = {
|
||||
/* Equal */
|
||||
{
|
||||
{ "2020-03-05T00:00:00", "2020-03-10T00:00:00" },
|
||||
{ "2020-03-05T00:00:00", "2020-03-10T00:00:00" },
|
||||
GCAL_RANGE_EQUAL,
|
||||
GCAL_RANGE_MATCH,
|
||||
},
|
||||
|
||||
/* No Overlap */
|
||||
{
|
||||
{ "2020-03-05T00:00:00", "2020-03-10T00:00:00" },
|
||||
{ "2020-03-10T00:00:00", "2020-03-15T00:00:00" },
|
||||
GCAL_RANGE_NO_OVERLAP,
|
||||
GCAL_RANGE_BEFORE,
|
||||
},
|
||||
{
|
||||
{ "2020-03-10T00:00:00", "2020-03-15T00:00:00" },
|
||||
{ "2020-03-05T00:00:00", "2020-03-10T00:00:00" },
|
||||
GCAL_RANGE_NO_OVERLAP,
|
||||
GCAL_RANGE_AFTER,
|
||||
},
|
||||
};
|
||||
|
||||
static void
|
||||
range_calculate_overlap (void)
|
||||
{
|
||||
g_autoptr (GTimeZone) utc = NULL;
|
||||
gint i;
|
||||
|
||||
|
||||
utc = g_time_zone_new_utc ();
|
||||
for (i = 0; i < G_N_ELEMENTS (ranges); i++)
|
||||
{
|
||||
g_autoptr (GDateTime) second_start = NULL;
|
||||
g_autoptr (GDateTime) second_end = NULL;
|
||||
g_autoptr (GDateTime) first_start = NULL;
|
||||
g_autoptr (GDateTime) first_end = NULL;
|
||||
g_autoptr (GcalRange) range_a = NULL;
|
||||
g_autoptr (GcalRange) range_b = NULL;
|
||||
GcalRangePosition position;
|
||||
GcalRangeOverlap overlap;
|
||||
|
||||
first_start = g_date_time_new_from_iso8601 (ranges[i].first.start, utc);
|
||||
first_end = g_date_time_new_from_iso8601 (ranges[i].first.end, utc);
|
||||
second_start = g_date_time_new_from_iso8601 (ranges[i].second.start, utc);
|
||||
second_end = g_date_time_new_from_iso8601 (ranges[i].second.end, utc);
|
||||
|
||||
range_a = gcal_range_new (first_start, first_end, GCAL_RANGE_DEFAULT);
|
||||
g_assert_nonnull (range_a);
|
||||
|
||||
range_b = gcal_range_new (second_start, second_end, GCAL_RANGE_DEFAULT);
|
||||
g_assert_nonnull (range_b);
|
||||
|
||||
overlap = gcal_range_calculate_overlap (range_a, range_b, &position);
|
||||
g_assert_cmpint (overlap, ==, ranges[i].expected_overlap);
|
||||
g_assert_cmpint (position, ==, ranges[i].expected_position);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************************************/
|
||||
|
||||
gint
|
||||
main (gint argc,
|
||||
gchar *argv[])
|
||||
{
|
||||
g_setenv ("TZ", "UTC", TRUE);
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/range/new", range_new);
|
||||
g_test_add_func ("/range/invalid-ranges", range_invalid);
|
||||
g_test_add_func ("/range/calculate-overlap", range_calculate_overlap);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
Reference in New Issue
Block a user