feat: add record/category management, imports and exports

This commit is contained in:
noise 2026-03-06 16:13:31 +03:00
parent 4f95b6b274
commit c00b561d03
7 changed files with 1637 additions and 306 deletions

View File

@ -27,7 +27,7 @@ static void register_with_portal (void) {
G_DBUS_CALL_FLAGS_NONE, G_DBUS_CALL_FLAGS_NONE,
-1, NULL, &error); -1, NULL, &error);
if (result == NULL) { if (result == NULL) {
g_info ("portal register: %s", error->message); g_warning ("portal register: %s", error->message);
g_clear_error (&error); g_clear_error (&error);
} else { } else {
g_variant_unref (result); g_variant_unref (result);

View File

@ -301,8 +301,8 @@ static void setup_sni(MoeMojiApplication *self) {
GVariant *result = g_dbus_connection_call_sync( GVariant *result = g_dbus_connection_call_sync(
self->dbus_conn, "org.freedesktop.DBus", "/org/freedesktop/DBus", self->dbus_conn, "org.freedesktop.DBus", "/org/freedesktop/DBus",
"org.freedesktop.DBus", "NameHasOwner", "org.freedesktop.DBus", "NameHasOwner",
g_variant_new("(s)", "org.kde.StatusNotifierWatcher"), G_VARIANT_TYPE("(b)"), g_variant_new("(s)", "org.kde.StatusNotifierWatcher"),
G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); G_VARIANT_TYPE("(b)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
if (result == NULL) { if (result == NULL) {
g_warning("NameHasOwner check failed: %s", error->message); g_warning("NameHasOwner check failed: %s", error->message);
g_clear_error(&error); g_clear_error(&error);
@ -354,10 +354,10 @@ static void setup_sni(MoeMojiApplication *self) {
} }
const gchar *unique_name = g_dbus_connection_get_unique_name(self->dbus_conn); const gchar *unique_name = g_dbus_connection_get_unique_name(self->dbus_conn);
g_dbus_connection_call( g_dbus_connection_call(
self->dbus_conn, "org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher", self->dbus_conn, "org.kde.StatusNotifierWatcher",
"org.kde.StatusNotifierWatcher", "RegisterStatusNotifierItem", "/StatusNotifierWatcher", "org.kde.StatusNotifierWatcher",
g_variant_new("(s)", unique_name), NULL, G_DBUS_CALL_FLAGS_NONE, -1, "RegisterStatusNotifierItem", g_variant_new("(s)", unique_name), NULL,
NULL, NULL, NULL); G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
} }
static void on_shortcuts_activated(G_GNUC_UNUSED GDBusProxy *proxy, static void on_shortcuts_activated(G_GNUC_UNUSED GDBusProxy *proxy,
@ -593,8 +593,8 @@ static void moemoji_application_show_about(G_GNUC_UNUSED GSimpleAction *action,
GtkWindow *window = NULL; GtkWindow *window = NULL;
g_return_if_fail(MOEMOJI_IS_APPLICATION(self)); g_return_if_fail(MOEMOJI_IS_APPLICATION(self));
window = gtk_application_get_active_window(GTK_APPLICATION(self)); window = gtk_application_get_active_window(GTK_APPLICATION(self));
gtk_show_about_dialog(window, "program-name", "MoeMoji", "version", "0.1.0", gtk_show_about_dialog(window, "program-name", "MoeMoji", "version",
NULL); PACKAGE_VERSION, NULL);
} }
static void moemoji_application_class_init(MoeMojiApplicationClass *klass) { static void moemoji_application_class_init(MoeMojiApplicationClass *klass) {

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,16 @@ struct _MoeMojiWindow {
GPtrArray *category_widgets; GPtrArray *category_widgets;
GtkWidget *bottom_spacer; GtkWidget *bottom_spacer;
int active_chip_index; int active_chip_index;
GtkStack *view_stack;
GtkWidget *add_button;
GtkWidget *back_button;
GtkWidget *menu_button;
GtkEntry *category_name_entry;
GtkWidget *entry_text_view;
GtkWidget *category_save_button;
char *selected_category_dir;
gulong scroll_handler_id;
gulong page_size_handler_id;
}; };
G_BEGIN_DECLS G_BEGIN_DECLS

View File

@ -10,37 +10,84 @@
<child type="titlebar"> <child type="titlebar">
<object class="AdwHeaderBar" id="header_bar"> <object class="AdwHeaderBar" id="header_bar">
<property name="show-end-title-buttons">True</property> <property name="show-end-title-buttons">True</property>
<child type="start">
<object class="GtkButton" id="add_button">
<property name="icon-name">list-add-symbolic</property>
</object>
</child>
<child type="start">
<object class="GtkButton" id="back_button">
<property name="icon-name">go-previous-symbolic</property>
<property name="visible">False</property>
</object>
</child>
<child type="end">
<object class="GtkMenuButton" id="menu_button">
<property name="icon-name">open-menu-symbolic</property>
<property name="menu-model">primary_menu</property>
</object>
</child>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkBox" id="outer_box"> <object class="GtkStack" id="view_stack">
<property name="orientation">vertical</property> <property name="transition-type">slide-left</property>
<property name="transition-duration">200</property>
<child> <child>
<object class="GtkSearchEntry" id="search_entry"> <object class="GtkStackPage">
<property name="placeholder-text">Jump to category...</property> <property name="name">main</property>
<property name="margin-start">6</property> <property name="child">
<property name="margin-end">6</property> <object class="GtkBox" id="outer_box">
<property name="margin-top">6</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="kaomoji_scroll">
<property name="hscrollbar-policy">never</property>
<property name="vexpand">True</property>
<child>
<object class="GtkBox" id="content_box">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="margin-start">6</property> <child>
<property name="margin-end">6</property> <object class="GtkSearchEntry" id="search_entry">
<property name="margin-top">6</property> <property name="placeholder-text">Jump to category...</property>
<property name="margin-bottom">6</property> <property name="margin-start">6</property>
<property name="spacing">4</property> <property name="margin-end">6</property>
<property name="margin-top">6</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="kaomoji_scroll">
<property name="hscrollbar-policy">never</property>
<property name="vexpand">True</property>
<child>
<object class="GtkBox" id="content_box">
<property name="orientation">vertical</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="spacing">4</property>
</object>
</child>
</object>
</child>
</object> </object>
</child> </property>
</object> </object>
</child> </child>
</object> </object>
</child> </child>
</template> </template>
<menu id="primary_menu">
<section>
<item>
<attribute name="label" translatable="yes">Manage…</attribute>
<attribute name="action">win.manage</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">Export…</attribute>
<attribute name="action">win.export</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Import…</attribute>
<attribute name="action">win.import</attribute>
</item>
</section>
</menu>
</interface> </interface>

View File

@ -37,7 +37,7 @@
color: white; color: white;
} }
wallpaper-bg > * { .wallpaper-bg > * {
background-color: transparent; background-color: transparent;
} }
@ -59,3 +59,50 @@ wallpaper-bg > * {
background-color: rgba(255, 255, 255, 0.25); background-color: rgba(255, 255, 255, 0.25);
border-radius: 8px; border-radius: 8px;
} }
.add-choice-button {
min-width: 200px;
min-height: 48px;
font-size: 16px;
}
.add-form {
padding: 24px;
margin: 12px;
}
.entry-editor-frame {
background-color: rgba(0, 0, 0, 0.5);
border-radius: 12px;
overflow: hidden;
}
.entry-text-view, .entry-text-view text {
background-color: transparent;
color: white;
caret-color: white;
}
.manage-emote {
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 8px;
padding: 6px 10px;
}
.manage-delete {
min-width: 20px;
min-height: 20px;
padding: 2px;
font-size: 10px;
background-color: @destructive_bg_color;
color: @destructive_fg_color;
}
.manage-cat-delete {
min-width: 24px;
min-height: 24px;
padding: 2px;
background-color: @destructive_bg_color;
color: @destructive_fg_color;
}

View File

@ -1,294 +1,601 @@
#include "moemoji-internal.h"
#include "moemoji-window.h"
#include <glib.h> #include <glib.h>
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "moemoji-internal.h"
#include "moemoji-window.h"
static void static void test_display_name_underscores(void) {
test_display_name_underscores (void) char *name = make_display_name("happy_faces");
{ g_assert_cmpstr(name, ==, "Happy faces");
char *name = make_display_name ("happy_faces"); g_free(name);
g_assert_cmpstr (name, ==, "Happy faces");
g_free (name);
} }
static void static void test_display_name_no_underscores(void) {
test_display_name_no_underscores (void) char *name = make_display_name("animals");
{ g_assert_cmpstr(name, ==, "Animals");
char *name = make_display_name ("animals"); g_free(name);
g_assert_cmpstr (name, ==, "Animals");
g_free (name);
} }
static void static void test_display_name_already_upper(void) {
test_display_name_already_upper (void) char *name = make_display_name("Animals");
{ g_assert_cmpstr(name, ==, "Animals");
char *name = make_display_name ("Animals"); g_free(name);
g_assert_cmpstr (name, ==, "Animals");
g_free (name);
} }
static void static void test_display_name_single_char(void) {
test_display_name_single_char (void) char *name = make_display_name("x");
{ g_assert_cmpstr(name, ==, "X");
char *name = make_display_name ("x"); g_free(name);
g_assert_cmpstr (name, ==, "X");
g_free (name);
} }
static void static void test_display_name_empty(void) {
test_display_name_empty (void) char *name = make_display_name("");
{ g_assert_cmpstr(name, ==, "");
char *name = make_display_name (""); g_free(name);
g_assert_cmpstr (name, ==, "");
g_free (name);
} }
static void static void test_find_kaomoji_with_env(void) {
test_find_kaomoji_with_env (void) char *dir = find_kaomoji_dir();
{ g_assert_nonnull(dir);
char *dir = find_kaomoji_dir (); g_assert_true(g_file_test(dir, G_FILE_TEST_IS_DIR));
g_assert_nonnull (dir); g_free(dir);
g_assert_true (g_file_test (dir, G_FILE_TEST_IS_DIR));
g_free (dir);
} }
static gboolean find_kaomoji_bogus_passed = FALSE; static gboolean find_kaomoji_bogus_passed = FALSE;
static void static void run_find_kaomoji_bogus(void) {
run_find_kaomoji_bogus (void) g_setenv("MESON_SOURCE_ROOT", "/nonexistent", TRUE);
{ char *saved = g_get_current_dir();
g_setenv ("MESON_SOURCE_ROOT", "/nonexistent", TRUE); g_assert_true(g_chdir("/tmp") == 0);
char *saved = g_get_current_dir ();
g_assert_true (g_chdir ("/tmp") == 0);
char *dir = find_kaomoji_dir (); char *dir = find_kaomoji_dir();
g_free (dir); g_free(dir);
g_assert_true (g_chdir (saved) == 0); g_assert_true(g_chdir(saved) == 0);
g_free (saved); g_free(saved);
g_setenv ("MESON_SOURCE_ROOT", SRCDIR, TRUE); g_setenv("MESON_SOURCE_ROOT", SRCDIR, TRUE);
find_kaomoji_bogus_passed = TRUE; find_kaomoji_bogus_passed = TRUE;
} }
static void static void test_find_kaomoji_bogus(void) {
test_find_kaomoji_bogus (void) g_assert_true(find_kaomoji_bogus_passed);
{
g_assert_true (find_kaomoji_bogus_passed);
} }
static void static void test_sni_category(void) {
test_sni_category (void) GVariant *v =
{ sni_get_property(NULL, NULL, NULL, NULL, "Category", NULL, NULL);
GVariant *v = sni_get_property (NULL, NULL, NULL, NULL, "Category", NULL, NULL); g_assert_nonnull(v);
g_assert_nonnull (v); g_assert_cmpstr(g_variant_get_string(v, NULL), ==, "ApplicationStatus");
g_assert_cmpstr (g_variant_get_string (v, NULL), ==, "ApplicationStatus"); g_variant_unref(v);
g_variant_unref (v);
} }
static void static void test_sni_id(void) {
test_sni_id (void) GVariant *v = sni_get_property(NULL, NULL, NULL, NULL, "Id", NULL, NULL);
{ g_assert_nonnull(v);
GVariant *v = sni_get_property (NULL, NULL, NULL, NULL, "Id", NULL, NULL); g_assert_cmpstr(g_variant_get_string(v, NULL), ==, "moemoji");
g_assert_nonnull (v); g_variant_unref(v);
g_assert_cmpstr (g_variant_get_string (v, NULL), ==, "moemoji");
g_variant_unref (v);
} }
static void static void test_sni_item_is_menu(void) {
test_sni_item_is_menu (void) GVariant *v =
{ sni_get_property(NULL, NULL, NULL, NULL, "ItemIsMenu", NULL, NULL);
GVariant *v = sni_get_property (NULL, NULL, NULL, NULL, "ItemIsMenu", NULL, NULL); g_assert_nonnull(v);
g_assert_nonnull (v); g_assert_false(g_variant_get_boolean(v));
g_assert_false (g_variant_get_boolean (v)); g_variant_unref(v);
g_variant_unref (v);
} }
static void static void test_sni_menu(void) {
test_sni_menu (void) GVariant *v = sni_get_property(NULL, NULL, NULL, NULL, "Menu", NULL, NULL);
{ g_assert_nonnull(v);
GVariant *v = sni_get_property (NULL, NULL, NULL, NULL, "Menu", NULL, NULL); g_assert_cmpstr(g_variant_get_string(v, NULL), ==, "/MenuBar");
g_assert_nonnull (v); g_variant_unref(v);
g_assert_cmpstr (g_variant_get_string (v, NULL), ==, "/MenuBar");
g_variant_unref (v);
} }
static void static void test_sni_unknown(void) {
test_sni_unknown (void) GVariant *v =
{ sni_get_property(NULL, NULL, NULL, NULL, "Nonexistent", NULL, NULL);
GVariant *v = sni_get_property (NULL, NULL, NULL, NULL, "Nonexistent", NULL, NULL); g_assert_null(v);
g_assert_null (v);
} }
static void static void test_dbusmenu_version(void) {
test_dbusmenu_version (void) GVariant *v =
{ dbusmenu_get_property(NULL, NULL, NULL, NULL, "Version", NULL, NULL);
GVariant *v = dbusmenu_get_property (NULL, NULL, NULL, NULL, "Version", NULL, NULL); g_assert_nonnull(v);
g_assert_nonnull (v); g_assert_cmpuint(g_variant_get_uint32(v), ==, 3);
g_assert_cmpuint (g_variant_get_uint32 (v), ==, 3); g_variant_unref(v);
g_variant_unref (v);
} }
static void static void test_dbusmenu_status(void) {
test_dbusmenu_status (void) GVariant *v =
{ dbusmenu_get_property(NULL, NULL, NULL, NULL, "Status", NULL, NULL);
GVariant *v = dbusmenu_get_property (NULL, NULL, NULL, NULL, "Status", NULL, NULL); g_assert_nonnull(v);
g_assert_nonnull (v); g_assert_cmpstr(g_variant_get_string(v, NULL), ==, "normal");
g_assert_cmpstr (g_variant_get_string (v, NULL), ==, "normal"); g_variant_unref(v);
g_variant_unref (v);
} }
static void static void test_dbusmenu_text_direction(void) {
test_dbusmenu_text_direction (void) GVariant *v = dbusmenu_get_property(NULL, NULL, NULL, NULL, "TextDirection",
{ NULL, NULL);
GVariant *v = dbusmenu_get_property (NULL, NULL, NULL, NULL, "TextDirection", NULL, NULL); g_assert_nonnull(v);
g_assert_nonnull (v); g_assert_cmpstr(g_variant_get_string(v, NULL), ==, "ltr");
g_assert_cmpstr (g_variant_get_string (v, NULL), ==, "ltr"); g_variant_unref(v);
g_variant_unref (v);
} }
static void static void test_dbusmenu_unknown(void) {
test_dbusmenu_unknown (void) GVariant *v =
{ dbusmenu_get_property(NULL, NULL, NULL, NULL, "Bogus", NULL, NULL);
GVariant *v = dbusmenu_get_property (NULL, NULL, NULL, NULL, "Bogus", NULL, NULL); g_assert_null(v);
g_assert_null (v);
} }
static void static void test_category_widgets_chip_init(void) {
test_category_widgets_chip_init (void) CategoryWidgets *cw = g_new0(CategoryWidgets, 1);
{ g_assert_null(cw->chip);
CategoryWidgets *cw = g_new0 (CategoryWidgets, 1); g_assert_null(cw->header);
g_assert_null (cw->chip); g_assert_null(cw->flow);
g_assert_null (cw->header); g_assert_null(cw->name);
g_assert_null (cw->flow); g_free(cw);
g_assert_null (cw->name);
g_free (cw);
} }
G_GNUC_INTERNAL GResource *moemoji_get_resource (void); G_GNUC_INTERNAL GResource *moemoji_get_resource(void);
static GResource *test_res = NULL; static GResource *test_res = NULL;
static GtkWidget *test_win = NULL; static GtkWidget *test_win = NULL;
static MoeMojiWindow *test_self = NULL; static MoeMojiWindow *test_self = NULL;
static void static void window_test_setup(void) {
window_test_setup (void) if (!test_res) {
{ test_res = moemoji_get_resource();
if (!test_res) { g_resources_register(test_res);
test_res = moemoji_get_resource (); }
g_resources_register (test_res); test_win = g_object_new(MOEMOJI_TYPE_WINDOW, NULL);
test_self = MOEMOJI_WINDOW(test_win);
}
static void window_test_teardown(void) {
gtk_window_destroy(GTK_WINDOW(test_win));
test_win = NULL;
test_self = NULL;
}
static void test_window_chip_setup(void) {
window_test_setup();
g_assert_cmpuint(test_self->category_widgets->len, >, 0);
for (guint i = 0; i < test_self->category_widgets->len; i++) {
CategoryWidgets *cw = g_ptr_array_index(test_self->category_widgets, i);
g_assert_nonnull(cw->chip);
}
window_test_teardown();
}
static void test_window_chip_labels_match(void) {
window_test_setup();
for (guint i = 0; i < test_self->category_widgets->len; i++) {
CategoryWidgets *cw = g_ptr_array_index(test_self->category_widgets, i);
const char *chip_label = gtk_button_get_label(GTK_BUTTON(cw->chip));
g_assert_cmpstr(chip_label, ==, cw->name);
}
window_test_teardown();
}
static void test_window_initial_active_chip(void) {
window_test_setup();
g_assert_cmpint(test_self->active_chip_index, ==, -1);
window_test_teardown();
}
static void test_window_all_categories_visible(void) {
window_test_setup();
for (guint i = 0; i < test_self->category_widgets->len; i++) {
CategoryWidgets *cw = g_ptr_array_index(test_self->category_widgets, i);
g_assert_true(gtk_widget_get_visible(cw->header));
g_assert_true(gtk_widget_get_visible(cw->flow));
}
window_test_teardown();
}
static void test_window_bottom_spacer(void) {
window_test_setup();
g_assert_nonnull(test_self->bottom_spacer);
GtkWidget *last =
gtk_widget_get_last_child(GTK_WIDGET(test_self->content_box));
g_assert_true(last == test_self->bottom_spacer);
window_test_teardown();
}
static void test_window_view_stack_exists(void) {
window_test_setup();
g_assert_nonnull(test_self->view_stack);
g_assert_true(GTK_IS_STACK(test_self->view_stack));
const char *name = gtk_stack_get_visible_child_name(test_self->view_stack);
g_assert_cmpstr(name, ==, "main");
window_test_teardown();
}
static void test_window_add_button_navigates(void) {
window_test_setup();
g_assert_nonnull(test_self->add_button);
g_assert_nonnull(test_self->back_button);
g_assert_true(gtk_widget_get_visible(test_self->add_button));
g_assert_false(gtk_widget_get_visible(test_self->back_button));
g_signal_emit_by_name(test_self->add_button, "clicked");
const char *name = gtk_stack_get_visible_child_name(test_self->view_stack);
g_assert_cmpstr(name, ==, "add-choice");
g_assert_false(gtk_widget_get_visible(test_self->add_button));
g_assert_true(gtk_widget_get_visible(test_self->back_button));
g_signal_emit_by_name(test_self->back_button, "clicked");
name = gtk_stack_get_visible_child_name(test_self->view_stack);
g_assert_cmpstr(name, ==, "main");
g_assert_true(gtk_widget_get_visible(test_self->add_button));
g_assert_false(gtk_widget_get_visible(test_self->back_button));
window_test_teardown();
}
static void test_window_stack_pages_exist(void) {
window_test_setup();
g_assert_nonnull(gtk_stack_get_child_by_name(test_self->view_stack, "main"));
g_assert_nonnull(
gtk_stack_get_child_by_name(test_self->view_stack, "add-choice"));
g_assert_nonnull(
gtk_stack_get_child_by_name(test_self->view_stack, "add-category"));
g_assert_nonnull(
gtk_stack_get_child_by_name(test_self->view_stack, "pick-category"));
g_assert_nonnull(
gtk_stack_get_child_by_name(test_self->view_stack, "add-entry"));
g_assert_nonnull(
gtk_stack_get_child_by_name(test_self->view_stack, "manage"));
window_test_teardown();
}
static void test_add_category_creates_dir(void) {
char *cat_dir = g_build_filename(g_get_user_data_dir(), "moemoji", "kaomoji",
"ZZZ_test_category", NULL);
g_mkdir_with_parents(cat_dir, 0755);
window_test_setup();
gboolean found = FALSE;
for (guint i = 0; i < test_self->category_widgets->len; i++) {
CategoryWidgets *cw = g_ptr_array_index(test_self->category_widgets, i);
if (g_strcmp0(cw->name, "ZZZ test category") == 0) {
found = TRUE;
break;
} }
test_win = g_object_new (MOEMOJI_TYPE_WINDOW, NULL); }
test_self = MOEMOJI_WINDOW (test_win); g_assert_true(found);
window_test_teardown();
g_rmdir(cat_dir);
char *kaomoji_parent =
g_build_filename(g_get_user_data_dir(), "moemoji", "kaomoji", NULL);
g_rmdir(kaomoji_parent);
g_free(kaomoji_parent);
char *moemoji_parent =
g_build_filename(g_get_user_data_dir(), "moemoji", NULL);
g_rmdir(moemoji_parent);
g_free(moemoji_parent);
g_free(cat_dir);
} }
static void static void test_add_entry_creates_file(void) {
window_test_teardown (void) char *cat_dir = g_build_filename(g_get_user_data_dir(), "moemoji", "kaomoji",
{ "ZZZ_test_entry_cat", NULL);
gtk_window_destroy (GTK_WINDOW (test_win)); g_mkdir_with_parents(cat_dir, 0755);
test_win = NULL; char *fpath = g_build_filename(cat_dir, "1.txt", NULL);
test_self = NULL; g_file_set_contents(fpath, "(╯°□°)╯︵ ┻━┻\n", -1, NULL);
} window_test_setup();
gboolean found = FALSE;
static void for (guint i = 0; i < test_self->category_widgets->len; i++) {
test_window_chip_setup (void) CategoryWidgets *cw = g_ptr_array_index(test_self->category_widgets, i);
{ if (g_strcmp0(cw->name, "ZZZ test entry cat") == 0) {
window_test_setup (); GtkWidget *first = gtk_widget_get_first_child(cw->flow);
g_assert_nonnull(first);
g_assert_cmpuint (test_self->category_widgets->len, >, 0); found = TRUE;
break;
for (guint i = 0; i < test_self->category_widgets->len; i++) {
CategoryWidgets *cw = g_ptr_array_index (test_self->category_widgets, i);
g_assert_nonnull (cw->chip);
} }
}
g_assert_true(found);
window_test_teardown (); g_free(test_self->selected_category_dir);
test_self->selected_category_dir = g_strdup(cat_dir);
GtkTextBuffer *buf =
gtk_text_view_get_buffer(GTK_TEXT_VIEW(test_self->entry_text_view));
gtk_text_buffer_set_text(buf, "( ˘ω˘ )", -1);
GtkWidget *entry_page =
gtk_stack_get_child_by_name(test_self->view_stack, "add-entry");
GtkWidget *save_btn = gtk_widget_get_last_child(entry_page);
g_assert_nonnull(save_btn);
g_signal_emit_by_name(save_btn, "clicked");
char *fpath2 = g_build_filename(cat_dir, "2.txt", NULL);
g_assert_true(g_file_test(fpath2, G_FILE_TEST_EXISTS));
char *contents = NULL;
g_file_get_contents(fpath2, &contents, NULL, NULL);
g_assert_nonnull(contents);
g_strchomp(contents);
g_assert_cmpstr(contents, ==, "( ˘ω˘ )");
g_free(contents);
const char *name = gtk_stack_get_visible_child_name(test_self->view_stack);
g_assert_cmpstr(name, ==, "main");
window_test_teardown();
g_unlink(fpath);
g_unlink(fpath2);
g_rmdir(cat_dir);
char *kaomoji_parent =
g_build_filename(g_get_user_data_dir(), "moemoji", "kaomoji", NULL);
g_rmdir(kaomoji_parent);
g_free(kaomoji_parent);
char *moemoji_parent =
g_build_filename(g_get_user_data_dir(), "moemoji", NULL);
g_rmdir(moemoji_parent);
g_free(moemoji_parent);
g_free(cat_dir);
g_free(fpath);
g_free(fpath2);
} }
static void static void test_export_creates_archive(void) {
test_window_chip_labels_match (void) char *user_dir =
{ g_build_filename(g_get_user_data_dir(), "moemoji", "kaomoji", NULL);
window_test_setup (); char *cat_dir = g_build_filename(user_dir, "ZZZ_export_test", NULL);
g_mkdir_with_parents(cat_dir, 0755);
char *fpath = g_build_filename(cat_dir, "1.txt", NULL);
g_file_set_contents(fpath, "(ノ◕ヮ◕)ノ*:・゚✧\n", -1, NULL);
char *tmpdir = g_dir_make_tmp("moemoji-test-XXXXXX", NULL);
g_assert_nonnull(tmpdir);
char *archive_path = g_build_filename(tmpdir, "test-export.tar.gz", NULL);
GSubprocess *tar =
g_subprocess_new(G_SUBPROCESS_FLAGS_NONE, NULL, "tar", "czf",
archive_path, "-C", user_dir, ".", NULL);
g_assert_nonnull(tar);
g_assert_true(g_subprocess_wait_check(tar, NULL, NULL));
g_object_unref(tar);
g_assert_true(g_file_test(archive_path, G_FILE_TEST_EXISTS));
char *extract_dir = g_build_filename(tmpdir, "extracted", NULL);
g_mkdir_with_parents(extract_dir, 0755);
for (guint i = 0; i < test_self->category_widgets->len; i++) { GSubprocess *untar =
CategoryWidgets *cw = g_ptr_array_index (test_self->category_widgets, i); g_subprocess_new(G_SUBPROCESS_FLAGS_NONE, NULL, "tar", "xzf",
const char *chip_label = gtk_button_get_label (GTK_BUTTON (cw->chip)); archive_path, "-C", extract_dir, NULL);
g_assert_cmpstr (chip_label, ==, cw->name); g_assert_nonnull(untar);
g_assert_true(g_subprocess_wait_check(untar, NULL, NULL));
g_object_unref(untar);
char *extracted_file =
g_build_filename(extract_dir, "ZZZ_export_test", "1.txt", NULL);
g_assert_true(g_file_test(extracted_file, G_FILE_TEST_EXISTS));
char *contents = NULL;
g_file_get_contents(extracted_file, &contents, NULL, NULL);
g_assert_nonnull(contents);
g_strchomp(contents);
g_assert_cmpstr(contents, ==, "(ノ◕ヮ◕)ノ*:・゚✧");
g_free(contents);
g_unlink(extracted_file);
g_free(extracted_file);
char *extracted_cat = g_build_filename(extract_dir, "ZZZ_export_test", NULL);
g_rmdir(extracted_cat);
g_free(extracted_cat);
g_rmdir(extract_dir);
g_free(extract_dir);
g_unlink(archive_path);
g_free(archive_path);
g_rmdir(tmpdir);
g_free(tmpdir);
g_unlink(fpath);
g_free(fpath);
g_rmdir(cat_dir);
g_free(cat_dir);
char *kaomoji_parent =
g_build_filename(g_get_user_data_dir(), "moemoji", "kaomoji", NULL);
g_rmdir(kaomoji_parent);
g_free(kaomoji_parent);
char *moemoji_parent =
g_build_filename(g_get_user_data_dir(), "moemoji", NULL);
g_rmdir(moemoji_parent);
g_free(moemoji_parent);
g_free(user_dir);
}
static void test_import_extracts_and_loads(void) {
char *src_dir = g_dir_make_tmp("moemoji-import-src-XXXXXX", NULL);
g_assert_nonnull(src_dir);
char *cat_dir = g_build_filename(src_dir, "ZZZ_import_test", NULL);
g_mkdir_with_parents(cat_dir, 0755);
char *fpath = g_build_filename(cat_dir, "1.txt", NULL);
g_file_set_contents(fpath, "ヽ(>∀<☆)\n", -1, NULL);
char *archive_path = g_build_filename(src_dir, "import-test.tar.gz", NULL);
GSubprocess *tar =
g_subprocess_new(G_SUBPROCESS_FLAGS_NONE, NULL, "tar", "czf",
archive_path, "-C", src_dir, "ZZZ_import_test", NULL);
g_assert_nonnull(tar);
g_assert_true(g_subprocess_wait_check(tar, NULL, NULL));
g_object_unref(tar);
char *user_dir =
g_build_filename(g_get_user_data_dir(), "moemoji", "kaomoji", NULL);
g_mkdir_with_parents(user_dir, 0755);
GSubprocess *untar =
g_subprocess_new(G_SUBPROCESS_FLAGS_NONE, NULL, "tar", "xzf",
archive_path, "-C", user_dir, NULL);
g_assert_nonnull(untar);
g_assert_true(g_subprocess_wait_check(untar, NULL, NULL));
g_object_unref(untar);
char *imported_file =
g_build_filename(user_dir, "ZZZ_import_test", "1.txt", NULL);
g_assert_true(g_file_test(imported_file, G_FILE_TEST_EXISTS));
char *contents = NULL;
g_file_get_contents(imported_file, &contents, NULL, NULL);
g_assert_nonnull(contents);
g_strchomp(contents);
g_assert_cmpstr(contents, ==, "ヽ(>∀<☆)");
g_free(contents);
window_test_setup();
gboolean found = FALSE;
for (guint i = 0; i < test_self->category_widgets->len; i++) {
CategoryWidgets *cw = g_ptr_array_index(test_self->category_widgets, i);
if (g_strcmp0(cw->name, "ZZZ import test") == 0) {
found = TRUE;
break;
} }
}
g_assert_true(found);
window_test_teardown();
window_test_teardown (); g_unlink(imported_file);
g_free(imported_file);
char *imported_cat = g_build_filename(user_dir, "ZZZ_import_test", NULL);
g_rmdir(imported_cat);
g_free(imported_cat);
g_rmdir(user_dir);
char *moemoji_parent =
g_build_filename(g_get_user_data_dir(), "moemoji", NULL);
g_rmdir(moemoji_parent);
g_free(moemoji_parent);
g_free(user_dir);
g_unlink(fpath);
g_free(fpath);
g_rmdir(cat_dir);
g_free(cat_dir);
g_unlink(archive_path);
g_free(archive_path);
GSubprocess *rm = g_subprocess_new(G_SUBPROCESS_FLAGS_NONE, NULL, "rm", "-rf",
src_dir, NULL);
if (rm) {
g_subprocess_wait(rm, NULL, NULL);
g_object_unref(rm);
}
g_free(src_dir);
} }
static void static void test_category_name_validation(void) {
test_window_initial_active_chip (void) window_test_setup();
{ gtk_editable_set_text(GTK_EDITABLE(test_self->category_name_entry), "");
window_test_setup (); g_assert_false(gtk_widget_get_sensitive(test_self->category_save_button));
gtk_editable_set_text(GTK_EDITABLE(test_self->category_name_entry), " ");
g_assert_cmpint (test_self->active_chip_index, ==, -1); g_assert_false(gtk_widget_get_sensitive(test_self->category_save_button));
gtk_editable_set_text(GTK_EDITABLE(test_self->category_name_entry),
window_test_teardown (); "Happy Faces");
g_assert_true(gtk_widget_get_sensitive(test_self->category_save_button));
gtk_editable_set_text(GTK_EDITABLE(test_self->category_name_entry),
"my-cool_category");
g_assert_true(gtk_widget_get_sensitive(test_self->category_save_button));
gtk_editable_set_text(GTK_EDITABLE(test_self->category_name_entry),
"bad/name");
g_assert_false(gtk_widget_get_sensitive(test_self->category_save_button));
gtk_editable_set_text(GTK_EDITABLE(test_self->category_name_entry),
"bad.name");
g_assert_false(gtk_widget_get_sensitive(test_self->category_save_button));
window_test_teardown();
} }
static void static void test_manage_search_entry(void) {
test_window_all_categories_visible (void) window_test_setup();
{ GtkWidget *page =
window_test_setup (); gtk_stack_get_child_by_name(test_self->view_stack, "manage");
g_assert_nonnull(page);
for (guint i = 0; i < test_self->category_widgets->len; i++) { GtkWidget *search = g_object_get_data(G_OBJECT(page), "manage-search");
CategoryWidgets *cw = g_ptr_array_index (test_self->category_widgets, i); g_assert_nonnull(search);
g_assert_true (gtk_widget_get_visible (cw->header)); g_assert_true(GTK_IS_SEARCH_ENTRY(search));
g_assert_true (gtk_widget_get_visible (cw->flow)); window_test_teardown();
}
window_test_teardown ();
} }
static void static void test_manage_populated(void) {
test_window_bottom_spacer (void) window_test_setup();
{ g_action_group_activate_action(G_ACTION_GROUP(test_self), "manage", NULL);
window_test_setup (); GtkWidget *page =
gtk_stack_get_child_by_name(test_self->view_stack, "manage");
g_assert_nonnull (test_self->bottom_spacer); GtkWidget *content = g_object_get_data(G_OBJECT(page), "manage-content");
GtkWidget *last = gtk_widget_get_last_child (GTK_WIDGET (test_self->content_box)); g_assert_nonnull(content);
g_assert_true (last == test_self->bottom_spacer); g_assert_nonnull(gtk_widget_get_first_child(content));
window_test_teardown();
window_test_teardown ();
} }
int static void test_manage_dupe_name_blocked(void) {
main (int argc, char *argv[]) window_test_setup();
{ g_assert_cmpuint(test_self->category_widgets->len, >, 0);
g_setenv ("MESON_SOURCE_ROOT", SRCDIR, TRUE); CategoryWidgets *cw = g_ptr_array_index(test_self->category_widgets, 0);
g_test_init (&argc, &argv, NULL); gtk_editable_set_text(GTK_EDITABLE(test_self->category_name_entry), cw->name);
g_assert_false(gtk_widget_get_sensitive(test_self->category_save_button));
run_find_kaomoji_bogus (); window_test_teardown();
}
gboolean have_display = gtk_init_check ();
int main(int argc, char *argv[]) {
g_test_add_func ("/display-name/underscores", test_display_name_underscores); g_setenv("MESON_SOURCE_ROOT", SRCDIR, TRUE);
g_test_add_func ("/display-name/no-underscores", test_display_name_no_underscores); g_test_init(&argc, &argv, NULL);
g_test_add_func ("/display-name/already-upper", test_display_name_already_upper);
g_test_add_func ("/display-name/single-char", test_display_name_single_char); run_find_kaomoji_bogus();
g_test_add_func ("/display-name/empty", test_display_name_empty);
g_test_add_func ("/find-kaomoji/with-env", test_find_kaomoji_with_env); gboolean have_display = gtk_init_check();
g_test_add_func ("/find-kaomoji/bogus", test_find_kaomoji_bogus);
g_test_add_func ("/sni/category", test_sni_category); g_test_add_func("/display-name/underscores", test_display_name_underscores);
g_test_add_func ("/sni/id", test_sni_id); g_test_add_func("/display-name/no-underscores",
g_test_add_func ("/sni/item-is-menu", test_sni_item_is_menu); test_display_name_no_underscores);
g_test_add_func ("/sni/menu", test_sni_menu); g_test_add_func("/display-name/already-upper",
g_test_add_func ("/sni/unknown", test_sni_unknown); test_display_name_already_upper);
g_test_add_func ("/dbusmenu/version", test_dbusmenu_version); g_test_add_func("/display-name/single-char", test_display_name_single_char);
g_test_add_func ("/dbusmenu/status", test_dbusmenu_status); g_test_add_func("/display-name/empty", test_display_name_empty);
g_test_add_func ("/dbusmenu/text-direction", test_dbusmenu_text_direction); g_test_add_func("/find-kaomoji/with-env", test_find_kaomoji_with_env);
g_test_add_func ("/dbusmenu/unknown", test_dbusmenu_unknown); g_test_add_func("/find-kaomoji/bogus", test_find_kaomoji_bogus);
g_test_add_func ("/category-widgets/chip-init", test_category_widgets_chip_init); g_test_add_func("/sni/category", test_sni_category);
if (have_display) { g_test_add_func("/sni/id", test_sni_id);
g_test_add_func ("/window/chip-setup", test_window_chip_setup); g_test_add_func("/sni/item-is-menu", test_sni_item_is_menu);
g_test_add_func ("/window/chip-labels-match", test_window_chip_labels_match); g_test_add_func("/sni/menu", test_sni_menu);
g_test_add_func ("/window/initial-active-chip", test_window_initial_active_chip); g_test_add_func("/sni/unknown", test_sni_unknown);
g_test_add_func ("/window/all-categories-visible", test_window_all_categories_visible); g_test_add_func("/dbusmenu/version", test_dbusmenu_version);
g_test_add_func ("/window/bottom-spacer", test_window_bottom_spacer); g_test_add_func("/dbusmenu/status", test_dbusmenu_status);
} g_test_add_func("/dbusmenu/text-direction", test_dbusmenu_text_direction);
g_test_add_func("/dbusmenu/unknown", test_dbusmenu_unknown);
return g_test_run (); g_test_add_func("/category-widgets/chip-init",
test_category_widgets_chip_init);
if (have_display) {
g_test_add_func("/window/chip-setup", test_window_chip_setup);
g_test_add_func("/window/chip-labels-match", test_window_chip_labels_match);
g_test_add_func("/window/initial-active-chip",
test_window_initial_active_chip);
g_test_add_func("/window/all-categories-visible",
test_window_all_categories_visible);
g_test_add_func("/window/bottom-spacer", test_window_bottom_spacer);
g_test_add_func("/window/view-stack-exists", test_window_view_stack_exists);
g_test_add_func("/window/add-button-navigates",
test_window_add_button_navigates);
g_test_add_func("/window/stack-pages-exist", test_window_stack_pages_exist);
g_test_add_func("/window/category-name-validation",
test_category_name_validation);
g_test_add_func("/window/add-category-creates-dir",
test_add_category_creates_dir);
g_test_add_func("/window/add-entry-creates-file",
test_add_entry_creates_file);
g_test_add_func("/window/export-creates-archive",
test_export_creates_archive);
g_test_add_func("/window/import-extracts-and-loads",
test_import_extracts_and_loads);
g_test_add_func("/window/manage-search-entry", test_manage_search_entry);
g_test_add_func("/window/manage-populated", test_manage_populated);
g_test_add_func("/window/manage-dupe-name-blocked",
test_manage_dupe_name_blocked);
}
return g_test_run();
} }