Compare commits

...

7 Commits

Author SHA1 Message Date
Benjamin Otte
df8475155c testsuite: Validate the section changing sortmodel 2023-05-28 02:08:43 +02:00
Benjamin Otte
e2547be696 testsuite: Make the listmodel validator validate sections 2023-05-28 02:08:43 +02:00
Benjamin Otte
c696ff6f3c testsuite: Split the listmodel validator out
The test got quite unwieldy - plus, we might want to reuse it elsewhere.
2023-05-28 02:08:43 +02:00
Benjamin Otte
4cdc078db4 testsuite: Make sorter generation random
Instead of getting the whole string from every item, get a substring at
offset and length that is randomly chosen when creating the sorter.

This generates way weirder and unpredictable sorting behavior, when each
sorter selects a few random characters to sort by that may or may not be
related between sorters.

The sorters can also have a very high chance of the comparison being
different (when comparing the whole string) or being equal (when
comparing the 4th letter where 3/4 of strings are <= 3 letters).
2023-05-28 02:08:43 +02:00
Benjamin Otte
59f801a02a testsuite: Fix test to actually work
The test was using the same sorter in both the outher and inner loop,
thereby making everything moot.
It even compared the wrong models and everything went fine.

Fix that.
2023-05-28 02:08:43 +02:00
Benjamin Otte
f4106a8fe2 testsuite: Get more complex strings
Instead of just a single character, generate up to 4 characters.
2023-05-28 02:08:43 +02:00
Benjamin Otte
5abaa9c989 stringsorter: Handle NULL strings
NULL is a valid string GValue, so we need to handle it.
The utf8 normalizing and collating functions do not, so we better catch
it early.
2023-05-28 02:08:43 +02:00
5 changed files with 699 additions and 128 deletions

View File

@@ -77,6 +77,11 @@ gtk_string_sorter_get_key (GtkExpression *expression,
return NULL;
string = g_value_get_string (&value);
if (string == NULL)
{
g_value_unset (&value);
return NULL;
}
if (ignore_case)
s = g_utf8_casefold (string, -1);

View File

@@ -0,0 +1,593 @@
/*
* Copyright © 2023 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtklistmodelvalidator.h"
#include "gtkprivate.h"
#define GDK_ARRAY_TYPE_NAME Items
#define GDK_ARRAY_NAME items
#define GDK_ARRAY_ELEMENT_TYPE gpointer
#define GDK_ARRAY_FREE_FUNC g_object_unref
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
#define GDK_ARRAY_TYPE_NAME Sections
#define GDK_ARRAY_NAME sections
#define GDK_ARRAY_ELEMENT_TYPE guint
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
/*<private>
* GtkListModelValidator:
*
* `GtkListModelValidator` is an object that can be attached to a given list model
* and ten checks that it conforms to the listmodel API guarantees.
*
* This is useful when implementing a new listmodel or when writing a testsuite.
*
* Note that these checks are expensive and can cause signficant slow downs. So it
* is recommended to only use them when testing.
*/
enum {
PROP_0,
PROP_MODEL,
NUM_PROPERTIES
};
typedef enum
{
GTK_LIST_VALIDATION_CHANGES = (1 << 0),
GTK_LIST_VALIDATION_SECTION_CHANGES = (1 << 1),
GTK_LIST_VALIDATION_MINIMAL_CHANGES = (1 << 2),
GTK_LIST_VALIDATION_N_ITEMS = (1 << 3),
GTK_LIST_VALIDATION_N_ITEMS_MINIMAL_NOTIFY = (1 << 4),
} GtkListValidationFlags;
struct _GtkListModelValidator
{
GObject parent_instance;
GListModel *model;
GtkListValidationFlags flags;
guint notified_n_items;
Items items;
Sections sections;
};
struct _GtkListModelValidatorClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
G_DEFINE_TYPE (GtkListModelValidator, gtk_list_model_validator, G_TYPE_OBJECT)
static void
gtk_list_model_validator_error (GtkListModelValidator *self,
GtkListValidationFlags flags,
const char *format,
...) G_GNUC_PRINTF(3, 4);
static void
gtk_list_model_validator_error (GtkListModelValidator *self,
GtkListValidationFlags flags,
const char *format,
...)
{
va_list args;
va_start (args, format);
g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, args);
va_end (args);
}
static void
gtk_list_model_validator_self_check_sections (GtkListModelValidator *self)
{
guint sum, i;
sum = 0;
for (i = 0; i < sections_get_size (&self->sections); i++)
{
g_assert (sections_get (&self->sections, i) > 0);
sum += sections_get (&self->sections, i);
}
g_assert (sum == items_get_size (&self->items));
}
static void
gtk_list_model_validator_validate_different (GtkListModelValidator *self,
guint self_position,
guint model_position)
{
gpointer o = g_list_model_get_item (self->model, model_position);
if (o == items_get (&self->items, self_position))
gtk_list_model_validator_error (self, GTK_LIST_VALIDATION_MINIMAL_CHANGES,
"item at position %u did not change but was part of items-changed",
self_position);
g_object_unref (o);
}
static gsize
gtk_list_model_validator_find_section_index (GtkListModelValidator *self,
guint position,
guint *offset)
{
gsize i;
for (i = 0; i < sections_get_size (&self->sections); i++)
{
guint items = sections_get (&self->sections, i);
if (position < items)
{
if (offset)
*offset = position;
return i;
}
position -= items;
}
if (offset)
*offset = position;
return i;
}
static void
gtk_list_model_validator_validate_section_range (GtkListModelValidator *self,
guint position,
guint n_items)
{
guint section, section_items, offset, start, end, i;
if (n_items == 0)
return;
section = gtk_list_model_validator_find_section_index (self, position, &offset);
section_items = sections_get (&self->sections, section);
for (i = 0; i < n_items; i++)
{
gtk_section_model_get_section (GTK_SECTION_MODEL (self->model), position + i, &start, &end);
if (start != position + i - offset ||
end != position + i - offset + section_items)
{
gtk_list_model_validator_error (self, GTK_LIST_VALIDATION_SECTION_CHANGES,
"item at %u reports wrong section: [%u, %u) but should be [%u, %u)",
position + i,
start, end,
position + i - offset, position + i - offset + section_items);
}
offset++;
if (offset == section_items)
{
section++;
if (section < sections_get_size (&self->sections))
section_items = sections_get (&self->sections, section);
else
section_items = 0;
offset = 0;
}
}
}
static void
gtk_list_model_validator_validate_range (GtkListModelValidator *self,
guint self_position,
guint model_position,
guint n_items)
{
guint i;
if (n_items == 0)
return;
for (i = 0; i < n_items; i++)
{
gpointer o = g_list_model_get_item (self->model, model_position + i);
if (o != items_get (&self->items, self_position + i))
gtk_list_model_validator_error (self, GTK_LIST_VALIDATION_CHANGES,
"item at %u did change but was not included in items-changed",
self_position + i);
g_object_unref (o);
}
}
static void
gtk_list_model_validator_invalidate_sections (GtkListModelValidator *self,
guint position,
guint removed,
guint added)
{
guint section, offset, remaining;
section = gtk_list_model_validator_find_section_index (self, position, &offset);
/* first, delete all the removed items from the sections */
remaining = removed;
while (remaining > 0)
{
guint section_items = sections_get (&self->sections, section);
if (remaining >= section_items - offset)
{
if (offset)
{
remaining -= section_items - offset;
*sections_index (&self->sections, section) = offset;
section++;
offset = 0;
}
else
{
remaining -= section_items;
sections_splice (&self->sections, section, 1, FALSE, NULL, 0);
}
}
else
{
*sections_index (&self->sections, section) -= remaining;
remaining = 0;
}
}
/* now add all the new items into their sections */
if (offset > 0 && sections_get (&self->sections, section) > offset)
{
*sections_index (&self->sections, section) += added;
}
else if (added > 0)
{
guint start, end;
guint pos = position;
remaining = added;
if (offset == 0 && section > 0)
{
section--;
offset = sections_get (&self->sections, section);
}
if (offset)
g_assert (offset == sections_get (&self->sections, section));
else
g_assert (section == 0);
gtk_section_model_get_section (GTK_SECTION_MODEL (self->model), pos, &start, &end);
if (start < pos)
{
g_assert (remaining >= end - start - offset);
remaining -= end - start - offset;
*sections_index (&self->sections, section) = end - start;
section++;
pos = end;
}
else if (offset)
section++;
while (remaining > 0)
{
gtk_section_model_get_section (GTK_SECTION_MODEL (self->model), pos, &start, &end);
if (end - start <= remaining)
{
sections_splice (&self->sections, section, 0, FALSE, (guint[1]) { end - start }, 1);
section++;
remaining -= end - start;
pos = end;
}
else
{
g_assert (end - start == remaining + sections_get (&self->sections, section));
*sections_index (&self->sections, section) = end - start;
section++;
remaining = 0;
}
}
}
gtk_list_model_validator_self_check_sections (self);
}
static void
gtk_list_model_validator_sections_changed_cb (GtkSectionModel *model,
guint position,
guint n_items,
GtkListModelValidator *self)
{
gtk_list_model_validator_invalidate_sections (self, position, n_items, n_items);
gtk_list_model_validator_validate_section_range (self, 0, items_get_size (&self->items));
}
static void
gtk_list_model_validator_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkListModelValidator *self)
{
guint i;
if (self->notified_n_items != items_get_size (&self->items))
gtk_list_model_validator_error (self, GTK_LIST_VALIDATION_N_ITEMS,
"notify::n-items wasn't emitted before new modifications: %u, should be %zu",
self->notified_n_items, items_get_size (&self->items));
gtk_list_model_validator_validate_range (self, 0, 0, position);
gtk_list_model_validator_validate_range (self,
position + removed,
position + added,
items_get_size (&self->items) - removed - position);
if (removed > 0 && added > 0)
{
gtk_list_model_validator_validate_different (self, position, position);
gtk_list_model_validator_validate_different (self, position + removed - 1, position + added - 1);
}
items_splice (&self->items, position, removed, FALSE, NULL, added);
for (i = 0; i < added; i++)
{
*items_index (&self->items, position + i) = g_list_model_get_item (model, position + i);
}
if (GTK_IS_SECTION_MODEL (model))
{
gtk_list_model_validator_invalidate_sections (self, position, removed, added);
/* Should we only validate unchanged items here?
* Because this also validates the newly added ones */
gtk_list_model_validator_validate_section_range (self, 0, items_get_size (&self->items));
}
}
static void
gtk_list_model_validator_notify_n_items_cb (GListModel *model,
GParamSpec *pspec,
GtkListModelValidator *self)
{
guint new_n_items = g_list_model_get_n_items (model);
if (items_get_size (&self->items) != new_n_items)
gtk_list_model_validator_error (self, GTK_LIST_VALIDATION_N_ITEMS,
"notify::n-items reports wrong item count: %u, should be %zu",
new_n_items, items_get_size (&self->items));
if (self->notified_n_items == new_n_items)
gtk_list_model_validator_error (self, GTK_LIST_VALIDATION_N_ITEMS_MINIMAL_NOTIFY,
"notify::n-items unchanged from last emission: %u items",
new_n_items);
self->notified_n_items = new_n_items;
}
static void
gtk_list_model_validator_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkListModelValidator *self = GTK_LIST_MODEL_VALIDATOR (object);
switch (prop_id)
{
case PROP_MODEL:
gtk_list_model_validator_set_model (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_list_model_validator_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkListModelValidator *self = GTK_LIST_MODEL_VALIDATOR (object);
switch (prop_id)
{
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_list_model_validator_weak_unref_cb (gpointer data,
GObject *not_the_model_anymore)
{
GtkListModelValidator *self = data;
g_assert (G_OBJECT (self->model) == not_the_model_anymore);
items_set_size (&self->items, 0);
self->model = NULL;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
g_object_unref (self);
}
static void
gtk_list_model_validator_clear_model (GtkListModelValidator *self)
{
if (self->model == NULL)
return;
items_set_size (&self->items, 0);
sections_set_size (&self->sections, 0);
g_signal_handlers_disconnect_by_func (self->model, gtk_list_model_validator_items_changed_cb, self);
g_signal_handlers_disconnect_by_func (self->model, gtk_list_model_validator_notify_n_items_cb, self);
if (GTK_IS_SECTION_MODEL (self->model))
g_signal_handlers_disconnect_by_func (self->model, gtk_list_model_validator_sections_changed_cb, self);
g_object_weak_unref (G_OBJECT (self->model), gtk_list_model_validator_weak_unref_cb, self);
self->model = NULL;
}
static void
gtk_list_model_validator_dispose (GObject *object)
{
GtkListModelValidator *self = GTK_LIST_MODEL_VALIDATOR (object);
g_assert (self->model == NULL);
G_OBJECT_CLASS (gtk_list_model_validator_parent_class)->dispose (object);
};
static void
gtk_list_model_validator_class_init (GtkListModelValidatorClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_list_model_validator_set_property;
gobject_class->get_property = gtk_list_model_validator_get_property;
gobject_class->dispose = gtk_list_model_validator_dispose;
/*<private>
* GtkListModelValidator:model: (attributes org.gtk.Property.get=gtk_list_model_validator_get_model org.gtk.Property.set=gtk_list_model_validator_set_model)
*
* Child model to validate.
*/
properties[PROP_MODEL] =
g_param_spec_object ("model", NULL, NULL,
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_list_model_validator_init (GtkListModelValidator *self)
{
items_init (&self->items);
sections_init (&self->sections);
}
/*<private>
* gtk_list_model_validator_new:
* @model: (transfer full) (nullable): The model to use
*
* Creates a new validation model.
*
* Returns: A new `GtkListModelValidator`
*/
GtkListModelValidator *
gtk_list_model_validator_new (GListModel *model)
{
GtkListModelValidator *self;
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
self = g_object_new (GTK_TYPE_LIST_MODEL_VALIDATOR,
"model", model,
NULL);
/* consume the reference */
g_clear_object (&model);
return self;
}
/*<private>
* gtk_list_model_validator_set_model: (attributes org.gtk.Method.set_property=model)
* @self: a `GtkListModelValidator`
* @model: (nullable): The model to be validated
*
* Sets the model to validate.
*/
void
gtk_list_model_validator_set_model (GtkListModelValidator *self,
GListModel *model)
{
guint i;
g_return_if_fail (GTK_IS_LIST_MODEL_VALIDATOR (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (self->model == model)
return;
if (self->model)
gtk_list_model_validator_clear_model (self);
else
g_object_ref (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_list_model_validator_items_changed_cb), self);
g_signal_connect (model, "notify::n-items", G_CALLBACK (gtk_list_model_validator_notify_n_items_cb), self);
if (GTK_IS_SECTION_MODEL (model))
g_signal_connect (model, "sections-changed", G_CALLBACK (gtk_list_model_validator_sections_changed_cb), self);
g_object_weak_ref (G_OBJECT (model), gtk_list_model_validator_weak_unref_cb, self);
self->notified_n_items = g_list_model_get_n_items (G_LIST_MODEL (model));
items_set_size (&self->items, self->notified_n_items);
for (i = 0; i < self->notified_n_items; i++)
{
*items_index (&self->items, i) = g_list_model_get_item (model, i);
}
if (GTK_IS_SECTION_MODEL (self->model))
{
guint start, end;
for (i = 0; i < self->notified_n_items; i = end)
{
gtk_section_model_get_section (GTK_SECTION_MODEL (self->model), i, &start, &end);
sections_append (&self->sections, end - start);
}
}
}
else
{
g_object_unref (self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/*<private>
* gtk_list_model_validator_get_model: (attributes org.gtk.Method.get_property=model)
* @self: a `GtkListModelValidator`
*
* Gets the model that is currently being used or %NULL if none.
*
* Returns: (nullable) (transfer none): The model in use
*/
GListModel *
gtk_list_model_validator_get_model (GtkListModelValidator *self)
{
g_return_val_if_fail (GTK_IS_LIST_MODEL_VALIDATOR (self), NULL);
return self->model;
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright © 2023 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define GTK_TYPE_LIST_MODEL_VALIDATOR (gtk_list_model_validator_get_type ())
G_DECLARE_FINAL_TYPE (GtkListModelValidator, gtk_list_model_validator, GTK, LIST_MODEL_VALIDATOR, GObject)
GtkListModelValidator * gtk_list_model_validator_new (GListModel *model);
void gtk_list_model_validator_set_model (GtkListModelValidator *self,
GListModel *model);
GListModel * gtk_list_model_validator_get_model (GtkListModelValidator *self);
G_END_DECLS

View File

@@ -76,7 +76,12 @@ tests = [
{ 'name': 'slicelistmodel' },
{ 'name': 'sorter' },
{ 'name': 'sortlistmodel' },
{ 'name': 'sortlistmodel-exhaustive' },
{ 'name': 'sortlistmodel-exhaustive',
'sources': [
'sortlistmodel-exhaustive.c',
'gtklistmodelvalidator.c',
],
},
{ 'name': 'spinbutton' },
{ 'name': 'stringlist' },
{ 'name': 'templates' },

View File

@@ -18,6 +18,9 @@
#include <locale.h>
#include <gtk/gtk.h>
#include "gtklistmodelvalidator.h"
#define MAX_CHARS 4
#define ensure_updated() G_STMT_START{ \
while (g_main_context_pending (NULL)) \
@@ -90,124 +93,6 @@ model_to_string (GListModel *model)
return g_string_free (string, FALSE);
}
static void
assert_items_changed_correctly (GListModel *model,
guint position,
guint removed,
guint added,
GListModel *compare)
{
guint i, n_items;
//sanity check that we got all notifies
g_assert_cmpuint (g_list_model_get_n_items (compare), ==, GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (compare), "last-notified-n-items")));
//g_print ("%s => %u -%u +%u => %s\n", model_to_string (compare), position, removed, added, model_to_string (model));
g_assert_cmpuint (g_list_model_get_n_items (model), ==, g_list_model_get_n_items (compare) - removed + added);
n_items = g_list_model_get_n_items (model);
if (position != 0 || removed != n_items)
{
/* Check that all unchanged items are indeed unchanged */
for (i = 0; i < position; i++)
{
gpointer o1 = g_list_model_get_item (model, i);
gpointer o2 = g_list_model_get_item (compare, i);
g_assert_cmphex (GPOINTER_TO_SIZE (o1), ==, GPOINTER_TO_SIZE (o2));
g_object_unref (o1);
g_object_unref (o2);
}
for (i = position + added; i < n_items; i++)
{
gpointer o1 = g_list_model_get_item (model, i);
gpointer o2 = g_list_model_get_item (compare, i - added + removed);
g_assert_cmphex (GPOINTER_TO_SIZE (o1), ==, GPOINTER_TO_SIZE (o2));
g_object_unref (o1);
g_object_unref (o2);
}
/* Check that the first and last added item are different from
* first and last removed item.
* Otherwise we could have kept them as-is
*/
if (removed > 0 && added > 0)
{
gpointer o1 = g_list_model_get_item (model, position);
gpointer o2 = g_list_model_get_item (compare, position);
g_assert_cmphex (GPOINTER_TO_SIZE (o1), !=, GPOINTER_TO_SIZE (o2));
g_object_unref (o1);
g_object_unref (o2);
o1 = g_list_model_get_item (model, position + added - 1);
o2 = g_list_model_get_item (compare, position + removed - 1);
g_assert_cmphex (GPOINTER_TO_SIZE (o1), !=, GPOINTER_TO_SIZE (o2));
g_object_unref (o1);
g_object_unref (o2);
}
}
/* Finally, perform the same change as the signal indicates */
g_list_store_splice (G_LIST_STORE (compare), position, removed, NULL, 0);
for (i = position; i < position + added; i++)
{
gpointer item = g_list_model_get_item (G_LIST_MODEL (model), i);
g_list_store_insert (G_LIST_STORE (compare), i, item);
g_object_unref (item);
}
}
static void
assert_n_items_notified_properly (GListModel *model,
GParamSpec *pspec,
GListModel *compare)
{
g_assert_cmpuint (g_list_model_get_n_items (model), !=, GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (compare), "last-notified-n-items")));
/* These should hve been updated in items-changed, which should have been emitted first */
g_assert_cmpuint (g_list_model_get_n_items (model), ==, g_list_model_get_n_items (compare));
g_object_set_data (G_OBJECT (compare),
"last-notified-n-items",
GUINT_TO_POINTER (g_list_model_get_n_items (model)));
}
static GtkSortListModel *
sort_list_model_new (GListModel *source,
GtkSorter *sorter)
{
GtkSortListModel *model;
GListStore *check;
guint i;
model = gtk_sort_list_model_new (source, sorter);
check = g_list_store_new (G_TYPE_OBJECT);
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i++)
{
gpointer item = g_list_model_get_item (G_LIST_MODEL (model), i);
g_list_store_append (check, item);
g_object_unref (item);
}
g_signal_connect_data (model,
"items-changed",
G_CALLBACK (assert_items_changed_correctly),
check,
(GClosureNotify) g_object_unref,
0);
g_object_set_data (G_OBJECT (check),
"last-notified-n-items",
GUINT_TO_POINTER (g_list_model_get_n_items (G_LIST_MODEL (check))));
g_signal_connect_data (model,
"notify::n-items",
G_CALLBACK (assert_n_items_notified_properly),
g_object_ref (check),
(GClosureNotify) g_object_unref,
0);
return model;
}
#define N_MODELS 8
static char *
@@ -242,10 +127,12 @@ create_sort_list_model (gconstpointer model_id,
GtkSortListModel *model;
guint id = GPOINTER_TO_UINT (model_id);
model = gtk_sort_list_model_new (((id & 1) || !source) ? NULL : g_object_ref (source), ((id & 2) || !sorter) ? NULL : g_object_ref (sorter));
if (track_changes)
model = sort_list_model_new (((id & 1) || !source) ? NULL : g_object_ref (source), ((id & 2) || !sorter) ? NULL : g_object_ref (sorter));
else
model = gtk_sort_list_model_new (((id & 1) || !source) ? NULL : g_object_ref (source), ((id & 2) || !sorter) ? NULL : g_object_ref (sorter));
{
GtkListModelValidator *validator = gtk_list_model_validator_new (G_LIST_MODEL (model));
g_object_unref (validator);
}
switch (id >> 2)
{
@@ -272,7 +159,7 @@ create_sort_list_model (gconstpointer model_id,
static GListModel *
create_source_model (guint min_size, guint max_size)
{
const char *strings[] = { "A", "a", "B", "b" };
const char chars[] = { 'A', 'a', 'B', 'b' };
GtkStringList *list;
guint i, size;
@@ -280,13 +167,56 @@ create_source_model (guint min_size, guint max_size)
list = gtk_string_list_new (NULL);
for (i = 0; i < size; i++)
gtk_string_list_append (list, strings[g_test_rand_int_range (0, G_N_ELEMENTS (strings))]);
{
char string[MAX_CHARS + 1];
int j, string_len;
string_len = g_test_rand_int_range (1, MAX_CHARS + 1);
for (j = 0; j < string_len; j++)
{
string[j] = chars[g_test_rand_int_range (0, G_N_ELEMENTS (chars))];
}
string[j] = 0;
gtk_string_list_append (list, string);
}
return G_LIST_MODEL (list);
}
#define N_SORTERS 3
static char *
get_substring (GObject *this,
guint offset,
guint len)
{
const char *string;
if (!GTK_IS_STRING_OBJECT (this))
return NULL;
string = gtk_string_object_get_string (GTK_STRING_OBJECT (this));
if (offset >= strlen (string))
return NULL;
string += offset;
return g_strndup (string, len);
}
static GtkExpression *
create_random_substring_expression (void)
{
return gtk_cclosure_expression_new (G_TYPE_STRING,
NULL,
2,
(GtkExpression *[2]) {
gtk_constant_expression_new (G_TYPE_UINT, (guint) g_test_rand_int_range (0, MAX_CHARS)),
gtk_constant_expression_new (G_TYPE_UINT, (guint) g_test_rand_int_range (1, MAX_CHARS + 1)),
},
G_CALLBACK (get_substring),
NULL,
NULL);
}
static GtkSorter *
create_sorter (gsize id)
{
@@ -300,7 +230,7 @@ create_sorter (gsize id)
case 1:
case 2:
/* match all As, Bs and nothing */
sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string")));
sorter = GTK_SORTER (gtk_string_sorter_new (create_random_substring_expression ()));
if (id == 1)
gtk_string_sorter_set_ignore_case (GTK_STRING_SORTER (sorter), FALSE);
return sorter;
@@ -360,12 +290,12 @@ test_two_sorters (gconstpointer model_id)
for (j = 0; j < N_SORTERS; j++)
{
sorter = create_sorter (i);
sorter = create_sorter (j);
gtk_sort_list_model_set_sorter (model2, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (every), sorter);
ensure_updated ();
assert_model_equal (G_LIST_MODEL (model2), G_LIST_MODEL (compare));
assert_model_equal (G_LIST_MODEL (model1), G_LIST_MODEL (compare));
for (k = 0; k < 10; k++)
{
@@ -606,7 +536,7 @@ test_sections (gconstpointer model_id)
store = g_list_store_new (G_TYPE_OBJECT);
flatten = gtk_flatten_list_model_new (G_LIST_MODEL (store));
sort = create_sort_list_model (model_id, FALSE, G_LIST_MODEL (flatten), NULL);
sort = create_sort_list_model (model_id, TRUE, G_LIST_MODEL (flatten), NULL);
for (i = 0; i < 500; i++)
{