mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
ncpp: stop() resets _instance, add unit test #538
This commit is contained in:
parent
8e649ebe46
commit
8971bda0c2
@ -56,7 +56,7 @@ endif()
|
||||
|
||||
# global compiler flags
|
||||
add_compile_definitions(FORTIFY_SOURCE=2)
|
||||
add_compile_options(-Wall -Wextra -W -Wshadow -Wformat)
|
||||
add_compile_options(-Wall -Wextra -W -Wshadow -Wformat -fexceptions)
|
||||
|
||||
message(STATUS "Requested multimedia engine: ${USE_MULTIMEDIA}")
|
||||
message(STATUS "Requested build mode: ${CMAKE_BUILD_TYPE}")
|
||||
|
@ -81,20 +81,7 @@ namespace ncpp
|
||||
return notcurses_version ();
|
||||
}
|
||||
|
||||
// This is potentially dangerous, but alas necessary. It can cause other calls here to fail in a bad way, but we
|
||||
// need a way to report errors to std{out,err} in case of failure and that will work only if notcurses is
|
||||
// stopped, so...
|
||||
//
|
||||
bool stop ()
|
||||
{
|
||||
if (nc == nullptr)
|
||||
throw invalid_state_error (ncpp_invalid_state_message);
|
||||
|
||||
bool ret = !notcurses_stop (nc);
|
||||
nc = nullptr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
bool stop ();
|
||||
|
||||
bool can_fade () const noexcept
|
||||
{
|
||||
|
@ -145,6 +145,7 @@ int ncfdplane_destroy(ncfdplane* n){
|
||||
|
||||
static pid_t
|
||||
launch_pipe_process(int* pipe, int* pidfd){
|
||||
*pidfd = -1;
|
||||
int pipes[2];
|
||||
if(pipe2(pipes, O_CLOEXEC)){ // can't use O_NBLOCK here (affects client)
|
||||
return -1;
|
||||
@ -166,7 +167,6 @@ launch_pipe_process(int* pipe, int* pidfd){
|
||||
if(p == 0){ // child
|
||||
if(dup2(pipes[1], STDOUT_FILENO) < 0 || dup2(pipes[1], STDERR_FILENO) < 0){
|
||||
fprintf(stderr, "Couldn't dup() %d (%s)\n", pipes[1], strerror(errno));
|
||||
raise(SIGKILL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}else if(p > 0){ // parent
|
||||
@ -245,6 +245,7 @@ ncsubproc* ncsubproc_createv(ncplane* n, const ncsubproc_options* opts,
|
||||
if(ret == NULL){
|
||||
return NULL;
|
||||
}
|
||||
memset(ret, 0, sizeof(*ret));
|
||||
ret->pid = launch_pipe_process(&fd, &ret->pidfd);
|
||||
if(ret->pid == 0){
|
||||
execv(bin, arg);
|
||||
@ -273,11 +274,11 @@ ncsubproc* ncsubproc_createvp(ncplane* n, const ncsubproc_options* opts,
|
||||
if(ret == NULL){
|
||||
return NULL;
|
||||
}
|
||||
memset(ret, 0, sizeof(*ret));
|
||||
ret->pid = launch_pipe_process(&fd, &ret->pidfd);
|
||||
if(ret->pid == 0){
|
||||
execvp(bin, arg);
|
||||
fprintf(stderr, "Error execv()ing %s\n", bin);
|
||||
raise(SIGKILL);
|
||||
exit(EXIT_FAILURE);
|
||||
}else if(ret->pid < 0){
|
||||
free(ret);
|
||||
@ -302,6 +303,7 @@ ncsubproc* ncsubproc_createvpe(ncplane* n, const ncsubproc_options* opts,
|
||||
if(ret == NULL){
|
||||
return NULL;
|
||||
}
|
||||
memset(ret, 0, sizeof(*ret));
|
||||
ret->pid = launch_pipe_process(&fd, &ret->pidfd);
|
||||
if(ret->pid == 0){
|
||||
#ifdef __FreeBSD__
|
||||
|
@ -54,3 +54,21 @@ Plane* NotCurses::get_top () noexcept
|
||||
|
||||
return Plane::map_plane (top);
|
||||
}
|
||||
|
||||
// This is potentially dangerous, but alas necessary. It can cause other calls
|
||||
// here to fail in a bad way, but we need a way to report errors to
|
||||
// std{out,err} in case of failure and that will work only if notcurses is
|
||||
// stopped, so...
|
||||
bool NotCurses::stop ()
|
||||
{
|
||||
if (nc == nullptr)
|
||||
throw invalid_state_error (ncpp_invalid_state_message);
|
||||
|
||||
bool ret = !notcurses_stop (nc);
|
||||
nc = nullptr;
|
||||
|
||||
const std::lock_guard<std::mutex> lock (init_mutex);
|
||||
_instance = nullptr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -26,10 +26,12 @@ int run ()
|
||||
NotCurses nc;
|
||||
|
||||
const char *ncver = nc.version ();
|
||||
Plane plane (1, 1, 0, 0);
|
||||
Plot plot1 (plane);
|
||||
PlotU plot2 (plane);
|
||||
PlotD plot3 (plane);
|
||||
{
|
||||
Plane plane (1, 1, 0, 0);
|
||||
Plot plot1 (plane);
|
||||
PlotU plot2 (plane);
|
||||
PlotD plot3 (plane);
|
||||
}
|
||||
|
||||
nc.stop ();
|
||||
|
||||
|
@ -27,10 +27,12 @@ int run ()
|
||||
NotCurses nc;
|
||||
|
||||
const char *ncver = nc.version ();
|
||||
Plane plane (1, 1, 0, 0);
|
||||
Plot plot1 (plane);
|
||||
PlotU plot2 (plane);
|
||||
PlotD plot3 (plane);
|
||||
{
|
||||
Plane plane (1, 1, 0, 0);
|
||||
Plot plot1 (plane);
|
||||
PlotU plot2 (plane);
|
||||
PlotD plot3 (plane);
|
||||
}
|
||||
|
||||
nc.stop ();
|
||||
|
||||
|
@ -1,14 +1,4 @@
|
||||
int main(void) {
|
||||
if(setlocale(LC_ALL, "") == nullptr){
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
srand(time(nullptr));
|
||||
std::atomic_bool gameover = false;
|
||||
notcurses_options ncopts{};
|
||||
ncopts.flags = NCOPTION_INHIBIT_SETLOCALE;
|
||||
ncpp::NotCurses nc(ncopts);
|
||||
Tetris t{nc, gameover};
|
||||
std::thread tid(&Tetris::Ticker, &t);
|
||||
bool IOLoop(ncpp::NotCurses& nc, Tetris& t, std::atomic_bool& gameover) {
|
||||
ncpp::Plane* stdplane = nc.get_stdplane();
|
||||
char32_t input = 0;
|
||||
ncinput ni;
|
||||
@ -32,11 +22,27 @@ int main(void) {
|
||||
}
|
||||
ncmtx.unlock();
|
||||
}
|
||||
if(gameover || input == 'q'){ // FIXME signal it on 'q'
|
||||
gameover = true;
|
||||
tid.join();
|
||||
}else{
|
||||
return gameover || input == 'q';
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
if(setlocale(LC_ALL, "") == nullptr){
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
srand(time(nullptr));
|
||||
std::atomic_bool gameover = false;
|
||||
notcurses_options ncopts{};
|
||||
ncopts.flags = NCOPTION_INHIBIT_SETLOCALE;
|
||||
ncpp::NotCurses nc(ncopts);
|
||||
{
|
||||
Tetris t{nc, gameover};
|
||||
std::thread tid(&Tetris::Ticker, &t);
|
||||
if(IOLoop(nc, t, gameover)){
|
||||
gameover = true; // FIXME signal thread
|
||||
tid.join();
|
||||
}else{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
return nc.stop() ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
@ -15,4 +15,13 @@ TEST_CASE("Exceptions") {
|
||||
CHECK(nc.stop());
|
||||
}
|
||||
|
||||
// ncpp only allows one notcurses object at a time (why?)
|
||||
SUBCASE("OnlyOneNotCurses") {
|
||||
NotCurses nc;
|
||||
std::unique_ptr<NotCurses> nc2;
|
||||
// FIXME attempts to match ::init_error have failed thus far :/
|
||||
CHECK_THROWS(nc2.reset(new NotCurses()));
|
||||
CHECK(nc.stop());
|
||||
}
|
||||
|
||||
}
|
28
tests/Ncpp.cpp
Normal file
28
tests/Ncpp.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "main.h"
|
||||
|
||||
using namespace ncpp;
|
||||
|
||||
TEST_CASE("Ncpp"
|
||||
* doctest::description("Basic C++ wrapper tests")) {
|
||||
|
||||
// we ought be able to construct a NotCurses object with a nullptr FILE
|
||||
// or even just no argument (decays to nullptr).
|
||||
SUBCASE("ConstructNotCurses") {
|
||||
NotCurses nc;
|
||||
CHECK(nc.stop());
|
||||
}
|
||||
|
||||
SUBCASE("ConstructNotCursesNullFILE") {
|
||||
NotCurses ncnull(nullptr);
|
||||
CHECK(ncnull.stop());
|
||||
}
|
||||
|
||||
// we ought be able to get a new NotCurses object after stop()ping one.
|
||||
SUBCASE("ConstructNotCursesTwice") {
|
||||
NotCurses nc;
|
||||
CHECK(nc.stop());
|
||||
NotCurses ncnull(nullptr);
|
||||
CHECK(ncnull.stop());
|
||||
}
|
||||
|
||||
}
|
@ -28,6 +28,7 @@ TEST_CASE("Fade") {
|
||||
struct ncplane* n_ = notcurses_stdplane(nc_);
|
||||
REQUIRE(n_);
|
||||
if(!notcurses_canfade(nc_)){
|
||||
CHECK(0 == notcurses_stop(nc_));
|
||||
return;
|
||||
}
|
||||
REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
|
||||
|
@ -47,7 +47,9 @@ auto testfdeofdestroys(struct ncfdplane* n, int fderrno, void* curry) -> int {
|
||||
}
|
||||
|
||||
// test ncfdplanes and ncsubprocs
|
||||
TEST_CASE("FdsAndSubprocs") {
|
||||
TEST_CASE("FdsAndSubprocs"
|
||||
* doctest::description("Fdplanes and subprocedures")
|
||||
* doctest::timeout(10)) {
|
||||
notcurses_options nopts{};
|
||||
nopts.inhibit_alternate_screen = true;
|
||||
nopts.suppress_banner = true;
|
||||
|
@ -90,6 +90,10 @@ auto main(int argc, const char **argv) -> int {
|
||||
std::cerr << "Coudln't set locale based on user preferences!" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(getenv("TERM") == NULL){
|
||||
std::cerr << "TERM wasn't defined, exiting with success" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
doctest::Context context;
|
||||
|
||||
context.setOption("order-by", "name"); // sort the test cases by their name
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <doctest/doctest.h>
|
||||
#include <notcurses/notcurses.h>
|
||||
#include <ncpp/NotCurses.hh>
|
||||
#include <ncpp/_exceptions.hh>
|
||||
#include "internal.h"
|
||||
|
||||
auto find_data(const char* datum) -> char*;
|
||||
|
@ -1,16 +0,0 @@
|
||||
#include "main.h"
|
||||
|
||||
using namespace ncpp;
|
||||
|
||||
TEST_CASE("Ncpp") {
|
||||
|
||||
// we ought be able to construct a NotCurses object with a nullptr FILE
|
||||
// or even just no argument (decays to nullptr).
|
||||
SUBCASE("ConstructNullFILE") {
|
||||
NotCurses nc;
|
||||
CHECK(nc.stop());
|
||||
NotCurses ncnull(nullptr);
|
||||
CHECK(ncnull.stop());
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user