/* GObject - GLib Type, Object, Parameter and Signal Library
 * Copyright (C) 2001, 2003 Red Hat, Inc.
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * 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/>.
 */

#include <glib-object.h>

#include "testcommon.h"
#include "testmodule.h"

/* This test tests getting the default vtable for an interface
 * and the initialization and finalization of such default
 * interfaces.
 *
 * We test this both for static and for dynamic interfaces.
 */

/**********************************************************************
 * Static interface tests
 **********************************************************************/

typedef struct _TestStaticIfaceClass TestStaticIfaceClass;

struct _TestStaticIfaceClass
{
  GTypeInterface base_iface;
  guint val;
};

GType test_static_iface_get_type (void);
#define TEST_TYPE_STATIC_IFACE (test_static_iface_get_type ())

static void
test_static_iface_default_init (TestStaticIfaceClass *iface)
{
  iface->val = 42;
}

DEFINE_IFACE (TestStaticIface, test_static_iface,
              NULL, test_static_iface_default_init)

static void
test_static_iface (void)
{
  TestStaticIfaceClass *static_iface;

  /* Not loaded until we call ref for the first time */
  static_iface = g_type_default_interface_peek (TEST_TYPE_STATIC_IFACE);
  g_assert_null (static_iface);

  /* Ref loads */
  static_iface = g_type_default_interface_ref (TEST_TYPE_STATIC_IFACE);
  g_assert_nonnull (static_iface);
  g_assert_cmpint (static_iface->val, ==, 42);

  /* Peek then works */
  static_iface = g_type_default_interface_peek (TEST_TYPE_STATIC_IFACE);
  g_assert_nonnull (static_iface);
  g_assert_cmpint (static_iface->val, ==, 42);

  /* Unref does nothing */
  g_type_default_interface_unref (static_iface);

  /* And peek still works */
  static_iface = g_type_default_interface_peek (TEST_TYPE_STATIC_IFACE);
  g_assert_nonnull (static_iface);
  g_assert_cmpint (static_iface->val, ==, 42);
}

/**********************************************************************
 * Dynamic interface tests
 **********************************************************************/

typedef struct _TestDynamicIfaceClass TestDynamicIfaceClass;

struct _TestDynamicIfaceClass
{
  GTypeInterface base_iface;
  guint val;
};

static GType test_dynamic_iface_type;
static gboolean dynamic_iface_init = FALSE;

#define TEST_TYPE_DYNAMIC_IFACE (test_dynamic_iface_type)

static void
test_dynamic_iface_default_init (TestStaticIfaceClass *iface)
{
  dynamic_iface_init = TRUE;
  iface->val = 42;
}

static void
test_dynamic_iface_default_finalize (TestStaticIfaceClass *iface)
{
  dynamic_iface_init = FALSE;
}

static void
test_dynamic_iface_register (GTypeModule *module)
{
  const GTypeInfo iface_info =
    {
      sizeof (TestDynamicIfaceClass),
      (GBaseInitFunc)      NULL,
      (GBaseFinalizeFunc)  NULL,
      (GClassInitFunc)     test_dynamic_iface_default_init,
      (GClassFinalizeFunc) test_dynamic_iface_default_finalize,
      NULL,
      0,
      0,
      NULL,
      NULL
    };

  test_dynamic_iface_type =
    g_type_module_register_type (module, G_TYPE_INTERFACE,
                                 "TestDynamicIface", &iface_info, 0);
}

static void
module_register (GTypeModule *module)
{
  test_dynamic_iface_register (module);
}

static void
test_dynamic_iface (void)
{
  TestDynamicIfaceClass *dynamic_iface;

  test_module_new (module_register);

  /* Not loaded until we call ref for the first time */
  dynamic_iface = g_type_default_interface_peek (TEST_TYPE_DYNAMIC_IFACE);
  g_assert_null (dynamic_iface);

  /* Ref loads */
  dynamic_iface = g_type_default_interface_ref (TEST_TYPE_DYNAMIC_IFACE);
  g_assert_true (dynamic_iface_init);
  g_assert_nonnull (dynamic_iface);
  g_assert_cmpint (dynamic_iface->val, ==, 42);

  /* Peek then works */
  dynamic_iface = g_type_default_interface_peek (TEST_TYPE_DYNAMIC_IFACE);
  g_assert_nonnull (dynamic_iface);
  g_assert_cmpint (dynamic_iface->val, ==, 42);

  /* Unref causes finalize */
  g_type_default_interface_unref (dynamic_iface);
#if 0
  /* Disabled as unloading dynamic types is disabled.
   * See https://gitlab.gnome.org/GNOME/glib/-/issues/667 */
  g_assert_false (dynamic_iface_init);
#endif

  /* Peek returns NULL */
  dynamic_iface = g_type_default_interface_peek (TEST_TYPE_DYNAMIC_IFACE);
#if 0
  /* Disabled as unloading dynamic types is disabled.
   * See https://gitlab.gnome.org/GNOME/glib/-/issues/667 */
  g_assert_null (dynamic_iface);
#endif

  /* Ref reloads */
  dynamic_iface = g_type_default_interface_ref (TEST_TYPE_DYNAMIC_IFACE);
  g_assert_true (dynamic_iface_init);
  g_assert_nonnull (dynamic_iface);
  g_assert_cmpint (dynamic_iface->val, ==, 42);

  /* And Unref causes finalize once more*/
  g_type_default_interface_unref (dynamic_iface);
#if 0
  /* Disabled as unloading dynamic types is disabled.
   * See https://gitlab.gnome.org/GNOME/glib/-/issues/667 */
  g_assert_false (dynamic_iface_init);
#endif
}

int
main (int   argc,
      char *argv[])
{
  g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
                          G_LOG_LEVEL_WARNING |
                          G_LOG_LEVEL_CRITICAL);

  g_test_init (&argc, &argv, NULL);

  g_test_add_func ("/gobject/static-iface", test_static_iface);
  g_test_add_func ("/gobject/dynamic-iface", test_dynamic_iface);

  return g_test_run ();
}