[gnome-calendar/gbsneto/range: 1/12] Introduce GcalRange



commit 240ee91451b13aa07e95fc9ad6c088fe0bf4530c
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Sun Apr 12 20:33:38 2020 -0300

    Introduce GcalRange
    
    GcalRange is a new structure that represents a time range. It is
    always inclusive at the start, and exclusive at the end.

 src/core/gcal-range.c | 456 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/core/gcal-range.h |  93 ++++++++++
 src/meson.build       |   1 +
 tests/meson.build     |   1 +
 tests/test-range.c    | 161 ++++++++++++++++++
 5 files changed, 712 insertions(+)
---
diff --git a/src/core/gcal-range.c b/src/core/gcal-range.c
new file mode 100644
index 00000000..3c41e9ee
--- /dev/null
+++ b/src/core/gcal-range.c
@@ -0,0 +1,456 @@
+/* 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"
+
+struct _GcalRange
+{
+  gatomicrefcount     ref_count;
+
+  GDateTime          *range_start;
+  GDateTime          *range_end;
+};
+
+G_DEFINE_BOXED_TYPE (GcalRange, gcal_range, gcal_range_ref, gcal_range_unref)
+
+/**
+ * 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);
+
+  return copy;
+}
+
+static void
+gcal_range_free (GcalRange *self)
+{
+  g_assert (self);
+  g_assert (g_atomic_ref_count_compare (&self->ref_count, 0));
+
+  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)
+{
+  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));
+}
+
+
+/**
+ * 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)
+{
+  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;
+
+  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)
+{
+  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              |-----------|
+   *
+   */
+
+  a_start_b_start_diff = g_date_time_compare (a->range_start, b->range_start);
+  a_end_b_end_diff = g_date_time_compare (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 */
+            {
+              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 = g_date_time_compare (a->range_start, b->range_end);
+              a_end_b_start_diff = g_date_time_compare (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)
+{
+  gint result;
+
+  g_return_val_if_fail (a && b, 0);
+
+  result = g_date_time_compare (a->range_start, b->range_start);
+
+  if (result == 0)
+    result = g_date_time_compare (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)
+{
+  GDateTime *start;
+  GDateTime *end;
+
+  g_return_val_if_fail (a != NULL, NULL);
+  g_return_val_if_fail (b != NULL, NULL);
+
+  if (g_date_time_compare (a->range_start, b->range_start) < 0)
+    start = a->range_start;
+  else
+    start = b->range_start;
+
+  if (g_date_time_compare (a->range_end, b->range_end) > 0)
+    end = a->range_end;
+  else
+    end = b->range_end;
+
+  return gcal_range_new (start, end);
+}
+
+/**
+ * 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);
+}
diff --git a/src/core/gcal-range.h b/src/core/gcal-range.h
new file mode 100644
index 00000000..f57d826c
--- /dev/null
+++ b/src/core/gcal-range.h
@@ -0,0 +1,93 @@
+/* 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;
+
+GType                gcal_range_get_type                         (void) G_GNUC_CONST;
+
+GcalRange*           gcal_range_new                              (GDateTime          *range_start,
+                                                                  GDateTime          *range_end);
+GcalRange*           gcal_range_new_take                         (GDateTime          *range_start,
+                                                                  GDateTime          *range_end);
+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);
+
+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
diff --git a/src/meson.build b/src/meson.build
index 802b30e0..7a62c7af 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -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',
diff --git a/tests/meson.build b/tests/meson.build
index 23685b36..3f4ec599 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -35,6 +35,7 @@ tests = [
   'server',
   'discoverer',
   'event',
+  'range',
   'range-tree',
 ]
 
diff --git a/tests/test-range.c b/tests/test-range.c
new file mode 100644
index 00000000..d870768e
--- /dev/null
+++ b/tests/test-range.c
@@ -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);
+  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);
+  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);
+  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);
+  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);
+  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);
+      g_assert_nonnull (range_a);
+
+      range_b = gcal_range_new (second_start, second_end);
+      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 ();
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]