forked from 12to11/12to11
Update dmabuf and seat tests
* tests/dmabuf_test.c (enum test_kind): Add XBGR888_KIND. (test_names): Add that test. (LAST_TEST): Set to XBGR888_KIND. (verify_single_step, test_single_step, test_next_step): Implement that test, but keep it disabled for now. * tests/seat_test.c (struct test_subsurface): New struct. (enum test_kind): Add TEST_GRAB_KIND. (test_names): Add that test kind. (LAST_TEST): Set to TEST_GRAB_KIND. (make_test_subsurface): New function. (run_grab_test): New functions. (test_single_step): Implement new test. (expect_enter_event): Accept surface arg. (expect_surface_enter): Adjust accordingly. (handle_wl_callback_done, wait_frame_callback): New functions.
This commit is contained in:
parent
9e7af2fe4c
commit
45df6087d6
2 changed files with 321 additions and 10 deletions
|
@ -34,15 +34,17 @@ enum test_kind
|
||||||
{
|
{
|
||||||
ARGB8888_KIND,
|
ARGB8888_KIND,
|
||||||
ARGB8888_LINEAR_KIND,
|
ARGB8888_LINEAR_KIND,
|
||||||
|
XBGR8888_KIND,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *test_names[] =
|
static const char *test_names[] =
|
||||||
{
|
{
|
||||||
"argb8888",
|
"argb8888",
|
||||||
"argb8888_linear",
|
"argb8888_linear",
|
||||||
|
"xbgr8888",
|
||||||
};
|
};
|
||||||
|
|
||||||
#define LAST_TEST ARGB8888_LINEAR_KIND
|
#define LAST_TEST XBGR8888_KIND
|
||||||
|
|
||||||
struct test_params_data
|
struct test_params_data
|
||||||
{
|
{
|
||||||
|
@ -148,6 +150,11 @@ verify_single_step (enum test_kind kind)
|
||||||
"argb8888_linear.dump");
|
"argb8888_linear.dump");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case XBGR8888_KIND:
|
||||||
|
verify_image_data (display, test_surface_window,
|
||||||
|
"xbgr8888_implicit.dump");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -190,7 +197,7 @@ test_single_step (enum test_kind kind)
|
||||||
{
|
{
|
||||||
test_log ("skipping ARGB888 with linear modifier as"
|
test_log ("skipping ARGB888 with linear modifier as"
|
||||||
" that is not supported");
|
" that is not supported");
|
||||||
test_complete ();
|
test_single_step (XBGR8888_KIND);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer = create_rainbow_buffer (GBM_FORMAT_ARGB8888,
|
buffer = create_rainbow_buffer (GBM_FORMAT_ARGB8888,
|
||||||
|
@ -210,6 +217,36 @@ test_single_step (enum test_kind kind)
|
||||||
wl_surface_commit (wayland_surface);
|
wl_surface_commit (wayland_surface);
|
||||||
wl_buffer_destroy (buffer);
|
wl_buffer_destroy (buffer);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case XBGR8888_KIND:
|
||||||
|
|
||||||
|
/* XBGR8888 currently does not work due to a bug in glamor. */
|
||||||
|
#if 0
|
||||||
|
if (!is_format_supported (DRM_FORMAT_XBGR8888,
|
||||||
|
DRM_FORMAT_MOD_INVALID))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
test_log ("skipping XBGR8888 with implicit modifier as"
|
||||||
|
" that is not supported");
|
||||||
|
test_complete ();
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = create_rainbow_buffer (GBM_FORMAT_XBGR8888,
|
||||||
|
DRM_FORMAT_MOD_INVALID,
|
||||||
|
0x0000ff,
|
||||||
|
0x00ff00,
|
||||||
|
0xff0000);
|
||||||
|
|
||||||
|
if (!buffer)
|
||||||
|
report_test_failure ("failed to create XBGR8888 buffer");
|
||||||
|
|
||||||
|
wl_surface_attach (wayland_surface, buffer, 0, 0);
|
||||||
|
submit_surface_damage (wayland_surface, 0, 0,
|
||||||
|
INT_MAX, INT_MAX);
|
||||||
|
submit_frame_callback (wayland_surface, kind);
|
||||||
|
wl_surface_commit (wayland_surface);
|
||||||
|
wl_buffer_destroy (buffer);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,6 +259,10 @@ test_next_step (enum test_kind kind)
|
||||||
test_single_step (ARGB8888_LINEAR_KIND);
|
test_single_step (ARGB8888_LINEAR_KIND);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARGB8888_LINEAR_KIND:
|
||||||
|
test_single_step (XBGR8888_KIND);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,11 +82,21 @@ struct test_recorded_button_event
|
||||||
uint32_t button, state;
|
uint32_t button, state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct test_subsurface
|
||||||
|
{
|
||||||
|
/* The subsurface itself. */
|
||||||
|
struct wl_subsurface *subsurface;
|
||||||
|
|
||||||
|
/* The associated surface. */
|
||||||
|
struct wl_surface *surface;
|
||||||
|
};
|
||||||
|
|
||||||
enum test_kind
|
enum test_kind
|
||||||
{
|
{
|
||||||
MAP_WINDOW_KIND,
|
MAP_WINDOW_KIND,
|
||||||
TEST_ENTRY_KIND,
|
TEST_ENTRY_KIND,
|
||||||
TEST_CLICK_KIND,
|
TEST_CLICK_KIND,
|
||||||
|
TEST_GRAB_KIND,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *test_names[] =
|
static const char *test_names[] =
|
||||||
|
@ -94,18 +104,22 @@ static const char *test_names[] =
|
||||||
"map_window",
|
"map_window",
|
||||||
"test_entry",
|
"test_entry",
|
||||||
"test_click",
|
"test_click",
|
||||||
|
"test_grab",
|
||||||
};
|
};
|
||||||
|
|
||||||
#define LAST_TEST TEST_CLICK_KIND
|
#define LAST_TEST TEST_GRAB_KIND
|
||||||
#define TEST_SOURCE_DEVICE 4500000
|
#define TEST_SOURCE_DEVICE 4500000
|
||||||
|
|
||||||
/* The display. */
|
/* The display. */
|
||||||
static struct test_display *display;
|
static struct test_display *display;
|
||||||
|
|
||||||
|
/* The subcompositor. */
|
||||||
|
static struct wl_subcompositor *subcompositor;
|
||||||
|
|
||||||
/* Test interfaces. */
|
/* Test interfaces. */
|
||||||
static struct test_interface test_interfaces[] =
|
static struct test_interface test_interfaces[] =
|
||||||
{
|
{
|
||||||
/* No interfaces yet. */
|
{ "wl_subcompositor", &subcompositor, &wl_subcompositor_interface, 1, },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The test surface window. */
|
/* The test surface window. */
|
||||||
|
@ -125,11 +139,12 @@ static struct test_recorded_event *record_tail;
|
||||||
|
|
||||||
/* Forward declarations. */
|
/* Forward declarations. */
|
||||||
static void submit_surface_damage (struct wl_surface *, int, int, int, int);
|
static void submit_surface_damage (struct wl_surface *, int, int, int, int);
|
||||||
|
static void wait_frame_callback (struct wl_surface *);
|
||||||
static void expect_surface_enter (double, double);
|
static void expect_surface_enter (double, double);
|
||||||
static void expect_surface_motion (double, double);
|
static void expect_surface_motion (double, double);
|
||||||
static void record_events (void);
|
static void record_events (void);
|
||||||
static void expect_frame_event (void);
|
static void expect_frame_event (void);
|
||||||
static void expect_enter_event (double, double);
|
static void expect_enter_event (struct wl_surface *, double, double);
|
||||||
static void expect_motion_event (double, double);
|
static void expect_motion_event (double, double);
|
||||||
static void expect_leave_event (void);
|
static void expect_leave_event (void);
|
||||||
static void expect_button_event (int, int);
|
static void expect_button_event (int, int);
|
||||||
|
@ -161,6 +176,42 @@ test_get_root (void)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static struct test_subsurface *
|
||||||
|
make_test_subsurface (void)
|
||||||
|
{
|
||||||
|
struct test_subsurface *subsurface;
|
||||||
|
|
||||||
|
subsurface = malloc (sizeof *subsurface);
|
||||||
|
|
||||||
|
if (!subsurface)
|
||||||
|
goto error_1;
|
||||||
|
|
||||||
|
subsurface->surface
|
||||||
|
= wl_compositor_create_surface (display->compositor);
|
||||||
|
|
||||||
|
if (!subsurface->surface)
|
||||||
|
goto error_2;
|
||||||
|
|
||||||
|
subsurface->subsurface
|
||||||
|
= wl_subcompositor_get_subsurface (subcompositor,
|
||||||
|
subsurface->surface,
|
||||||
|
wayland_surface);
|
||||||
|
|
||||||
|
if (!subsurface->subsurface)
|
||||||
|
goto error_3;
|
||||||
|
|
||||||
|
return subsurface;
|
||||||
|
|
||||||
|
error_3:
|
||||||
|
wl_surface_destroy (subsurface->surface);
|
||||||
|
error_2:
|
||||||
|
free (subsurface);
|
||||||
|
error_1:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
run_click_test (struct test_XIButtonState *button_state)
|
run_click_test (struct test_XIButtonState *button_state)
|
||||||
{
|
{
|
||||||
|
@ -296,11 +347,131 @@ run_click_test (struct test_XIButtonState *button_state)
|
||||||
expect_no_events ();
|
expect_no_events ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
run_grab_test (struct test_XIButtonState *button_state,
|
||||||
|
struct test_subsurface *child)
|
||||||
|
{
|
||||||
|
/* First, get all events that should already have arrived. */
|
||||||
|
wl_display_roundtrip (display->display);
|
||||||
|
|
||||||
|
/* Next, dispatch all of the events. */
|
||||||
|
test_seat_controller_dispatch_XI_Enter (display->seat->controller,
|
||||||
|
test_get_time (),
|
||||||
|
TEST_SOURCE_DEVICE,
|
||||||
|
XINotifyAncestor,
|
||||||
|
test_get_root (),
|
||||||
|
test_surface_window,
|
||||||
|
None,
|
||||||
|
wl_fixed_from_double (0.0),
|
||||||
|
wl_fixed_from_double (0.0),
|
||||||
|
wl_fixed_from_double (0.0),
|
||||||
|
wl_fixed_from_double (0.0),
|
||||||
|
XINotifyNormal,
|
||||||
|
False, True, NULL, NULL,
|
||||||
|
NULL);
|
||||||
|
test_seat_controller_dispatch_XI_Motion (display->seat->controller,
|
||||||
|
test_get_time (),
|
||||||
|
TEST_SOURCE_DEVICE,
|
||||||
|
0,
|
||||||
|
test_get_root (),
|
||||||
|
test_surface_window,
|
||||||
|
None,
|
||||||
|
wl_fixed_from_double (0.0),
|
||||||
|
wl_fixed_from_double (0.0),
|
||||||
|
wl_fixed_from_double (0.0),
|
||||||
|
wl_fixed_from_double (0.0),
|
||||||
|
0,
|
||||||
|
button_state,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
test_seat_controller_dispatch_XI_Motion (display->seat->controller,
|
||||||
|
test_get_time (),
|
||||||
|
TEST_SOURCE_DEVICE,
|
||||||
|
0,
|
||||||
|
test_get_root (),
|
||||||
|
test_surface_window,
|
||||||
|
None,
|
||||||
|
wl_fixed_from_double (150.0),
|
||||||
|
wl_fixed_from_double (150.0),
|
||||||
|
wl_fixed_from_double (150.0),
|
||||||
|
wl_fixed_from_double (150.0),
|
||||||
|
0,
|
||||||
|
button_state,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
test_seat_controller_dispatch_XI_ButtonPress (display->seat->controller,
|
||||||
|
test_get_time (),
|
||||||
|
TEST_SOURCE_DEVICE,
|
||||||
|
1,
|
||||||
|
test_get_root (),
|
||||||
|
test_surface_window,
|
||||||
|
None,
|
||||||
|
wl_fixed_from_double (150.0),
|
||||||
|
wl_fixed_from_double (150.0),
|
||||||
|
wl_fixed_from_double (150.0),
|
||||||
|
wl_fixed_from_double (150.0),
|
||||||
|
0,
|
||||||
|
button_state,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
test_XIButtonState_add_button (button_state, 1);
|
||||||
|
test_seat_controller_dispatch_XI_Motion (display->seat->controller,
|
||||||
|
test_get_time (),
|
||||||
|
TEST_SOURCE_DEVICE,
|
||||||
|
0,
|
||||||
|
test_get_root (),
|
||||||
|
test_surface_window,
|
||||||
|
None,
|
||||||
|
wl_fixed_from_double (95.0),
|
||||||
|
wl_fixed_from_double (95.0),
|
||||||
|
wl_fixed_from_double (95.0),
|
||||||
|
wl_fixed_from_double (95.0),
|
||||||
|
0,
|
||||||
|
button_state,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
test_seat_controller_dispatch_XI_ButtonRelease (display->seat->controller,
|
||||||
|
test_get_time (),
|
||||||
|
TEST_SOURCE_DEVICE,
|
||||||
|
1,
|
||||||
|
test_get_root (),
|
||||||
|
test_surface_window,
|
||||||
|
None,
|
||||||
|
wl_fixed_from_double (95.0),
|
||||||
|
wl_fixed_from_double (90.0),
|
||||||
|
wl_fixed_from_double (95.0),
|
||||||
|
wl_fixed_from_double (95.0),
|
||||||
|
0,
|
||||||
|
button_state,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
test_XIButtonState_remove_button (button_state, 1);
|
||||||
|
|
||||||
|
/* Now, verify the events that arrive. */
|
||||||
|
record_events ();
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_enter_event (wayland_surface, 95.0, 95.0);
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_leave_event ();
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_button_event (BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_motion_event (-5.0, -5.0);
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_button_event (BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_motion_event (50.0, 50.0);
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_enter_event (child->surface, 50.0, 50.0);
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_leave_event ();
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_motion_event (0.0, 0.0);
|
||||||
|
expect_frame_event ();
|
||||||
|
expect_enter_event (wayland_surface, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_single_step (enum test_kind kind)
|
test_single_step (enum test_kind kind)
|
||||||
{
|
{
|
||||||
struct wl_buffer *buffer;
|
struct wl_buffer *buffer, *child_buffer;
|
||||||
struct test_XIButtonState *button_state;
|
struct test_XIButtonState *button_state;
|
||||||
|
struct test_subsurface *child;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
test_log ("running test step: %s", test_names[kind]);
|
test_log ("running test step: %s", test_names[kind]);
|
||||||
|
@ -316,7 +487,6 @@ test_single_step (enum test_kind kind)
|
||||||
wl_surface_attach (wayland_surface, buffer, 0, 0);
|
wl_surface_attach (wayland_surface, buffer, 0, 0);
|
||||||
submit_surface_damage (wayland_surface, 0, 0, 500, 500);
|
submit_surface_damage (wayland_surface, 0, 0, 500, 500);
|
||||||
wl_surface_commit (wayland_surface);
|
wl_surface_commit (wayland_surface);
|
||||||
wl_buffer_destroy (buffer);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEST_ENTRY_KIND:
|
case TEST_ENTRY_KIND:
|
||||||
|
@ -390,6 +560,61 @@ test_single_step (enum test_kind kind)
|
||||||
report_test_failure ("failed to obtain button state resource");
|
report_test_failure ("failed to obtain button state resource");
|
||||||
|
|
||||||
run_click_test (button_state);
|
run_click_test (button_state);
|
||||||
|
kind = TEST_GRAB_KIND;
|
||||||
|
goto again;
|
||||||
|
|
||||||
|
case TEST_GRAB_KIND:
|
||||||
|
/* Test subsurface grabbing. Create a 100x100 child of the
|
||||||
|
parent surface, and move it to 100, 100. Then, enter the
|
||||||
|
parent at 0, 0, and dispatch a motion event there. Dispatch
|
||||||
|
a motion event to 150, 150 (inside the child). Press button
|
||||||
|
1. Dispatch a motion event to 95, 95. Finally, release
|
||||||
|
button 1. Verify that only the following events are sent in
|
||||||
|
the given order:
|
||||||
|
|
||||||
|
enter (SERIAL, PARENT, 0.0, 0.0)
|
||||||
|
frame ()
|
||||||
|
motion (TIME, 0.0, 0.0)
|
||||||
|
frame ()
|
||||||
|
leave ()
|
||||||
|
frame ()
|
||||||
|
enter (SERIAL, CHILD, 50.0, 50.0)
|
||||||
|
frame ()
|
||||||
|
motion (TIME, 50.0, 50.0)
|
||||||
|
frame ()
|
||||||
|
button (SERIAL, TIME, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED)
|
||||||
|
frame ()
|
||||||
|
motion (TIME, -5.0, -5.0)
|
||||||
|
frame ()
|
||||||
|
button (SERIAL, TIME, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELASED)
|
||||||
|
frame ()
|
||||||
|
leave ()
|
||||||
|
frame ()
|
||||||
|
enter (SERIAL, PARENT, 95.0, 95.0)
|
||||||
|
frame () */
|
||||||
|
|
||||||
|
child = make_test_subsurface ();
|
||||||
|
|
||||||
|
if (!child)
|
||||||
|
report_test_failure ("failed to create test subsurface");
|
||||||
|
|
||||||
|
child_buffer = load_png_image (display, "seat_child.png");
|
||||||
|
|
||||||
|
if (!child_buffer)
|
||||||
|
report_test_failure ("failed to load seat_child.png");
|
||||||
|
|
||||||
|
wl_surface_attach (child->surface, child_buffer, 0, 0);
|
||||||
|
wl_surface_commit (child->surface);
|
||||||
|
wl_subsurface_set_position (child->subsurface, 100, 100);
|
||||||
|
|
||||||
|
/* wait_frame_callback is necessary because input regions are
|
||||||
|
not updated until the update completes. */
|
||||||
|
wait_frame_callback (wayland_surface);
|
||||||
|
|
||||||
|
/* Run the test. */
|
||||||
|
run_grab_test (button_state, child);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind == LAST_TEST)
|
if (kind == LAST_TEST)
|
||||||
|
@ -428,7 +653,7 @@ expect_frame_event (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
expect_enter_event (double x, double y)
|
expect_enter_event (struct wl_surface *surface, double x, double y)
|
||||||
{
|
{
|
||||||
struct test_recorded_event *event;
|
struct test_recorded_event *event;
|
||||||
struct test_recorded_enter_event *enter;
|
struct test_recorded_enter_event *enter;
|
||||||
|
@ -444,7 +669,8 @@ expect_enter_event (double x, double y)
|
||||||
{
|
{
|
||||||
enter = (struct test_recorded_enter_event *) event;
|
enter = (struct test_recorded_enter_event *) event;
|
||||||
|
|
||||||
if (enter->x == x && enter->y == y)
|
if (enter->x == x && enter->y == y
|
||||||
|
&& (!surface || (surface == enter->surface)))
|
||||||
free (event);
|
free (event);
|
||||||
else
|
else
|
||||||
report_test_failure ("expected enter event received "
|
report_test_failure ("expected enter event received "
|
||||||
|
@ -542,7 +768,7 @@ expect_surface_enter (double x, double y)
|
||||||
|
|
||||||
/* Expect an enter event, followed by a frame event. */
|
/* Expect an enter event, followed by a frame event. */
|
||||||
expect_frame_event ();
|
expect_frame_event ();
|
||||||
expect_enter_event (x, y);
|
expect_enter_event (NULL, x, y);
|
||||||
expect_no_events ();
|
expect_no_events ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,6 +1040,50 @@ submit_surface_damage (struct wl_surface *surface, int x, int y, int width,
|
||||||
wl_surface_damage (surface, x, y, width, height);
|
wl_surface_damage (surface, x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_wl_callback_done (void *data, struct wl_callback *callback,
|
||||||
|
uint32_t callback_data)
|
||||||
|
{
|
||||||
|
bool *flag;
|
||||||
|
|
||||||
|
wl_callback_destroy (callback);
|
||||||
|
|
||||||
|
/* Now tell wait_frame_callback to break out of the loop. */
|
||||||
|
flag = data;
|
||||||
|
*flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_callback_listener wl_callback_listener =
|
||||||
|
{
|
||||||
|
handle_wl_callback_done,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
wait_frame_callback (struct wl_surface *surface)
|
||||||
|
{
|
||||||
|
struct wl_callback *callback;
|
||||||
|
bool flag;
|
||||||
|
|
||||||
|
/* Commit surface and wait for a frame callback. */
|
||||||
|
|
||||||
|
callback = wl_surface_frame (surface);
|
||||||
|
flag = false;
|
||||||
|
|
||||||
|
wl_callback_add_listener (callback, &wl_callback_listener,
|
||||||
|
&flag);
|
||||||
|
wl_surface_commit (surface);
|
||||||
|
|
||||||
|
while (!flag)
|
||||||
|
{
|
||||||
|
if (wl_display_dispatch (display->display) == -1)
|
||||||
|
die ("wl_display_dispatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
run_test (void)
|
run_test (void)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue