Skip to content

Commit

Permalink
Add TAP tests for extension state
Browse files Browse the repository at this point in the history
Add a TAP test that checks that the extensions state is updated across
concurrent sessions/backends when the extension is "dropped" or
"created".
  • Loading branch information
erimatnor committed Mar 30, 2022
1 parent b0e34b6 commit 6cddc41
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 2 deletions.
17 changes: 17 additions & 0 deletions src/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,20 @@ ts_extension_is_proxy_table_relid(Oid relid)
{
return relid == extension_proxy_oid;
}

#if TS_DEBUG
static const char *extstate_str[] = {
[EXTENSION_STATE_UNKNOWN] = "unknown",
[EXTENSION_STATE_TRANSITIONING] = "transitioning",
[EXTENSION_STATE_CREATED] = "created",
[EXTENSION_STATE_NOT_INSTALLED] = "not installed",
};

TS_FUNCTION_INFO_V1(ts_extension_get_state);

Datum
ts_extension_get_state(PG_FUNCTION_ARGS)
{
PG_RETURN_TEXT_P(cstring_to_text(extstate_str[extstate]));
}
#endif
19 changes: 19 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,24 @@ elseif(REQUIRE_ALL_TESTS)
"All tests were required but 'pg_isolation_regress' could not be found")
endif()

if(TAP_CHECKS)
add_custom_target(
provecheck
COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/tmp_check
COMMAND
CONFDIR=${CMAKE_BINARY_DIR}/test PATH="${PG_BINDIR}:$ENV{PATH}"
PG_REGRESS=${PG_REGRESS} SRC_DIR=${PG_SOURCE_DIR}
CM_SRC_DIR=${CMAKE_SOURCE_DIR} PG_LIBDIR=${PG_LIBDIR}
${PRIMARY_TEST_DIR}/pg_prove.sh
USES_TERMINAL)
list(APPEND _install_checks provecheck)
elseif(REQUIRE_ALL_TESTS)
message(
FATAL_ERROR
"All tests were required but TAP_CHECKS was off (see previous messages why)"
)
endif()

# We add the installcheck target even when _install_checks is empty as tsl code
# might add dependencies to it even when regress checks are disabled.
add_custom_target(installcheck DEPENDS ${_install_checks})
Expand All @@ -94,6 +112,7 @@ add_custom_target(installchecklocal DEPENDS ${_local_install_checks})

add_subdirectory(sql)
add_subdirectory(isolation)
add_subdirectory(t)

if(PG_SOURCE_DIR)
add_subdirectory(pgtest)
Expand Down
97 changes: 97 additions & 0 deletions test/t/001_extension.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# This file and its contents are licensed under the Timescale License.
# Please see the included NOTICE for copyright information and
# LICENSE-TIMESCALE for a copy of the license.

use strict;
use warnings;
use TimescaleNode;
use TestLib;
use Data::Dumper;
use Test::More tests => 6;

# This test checks that the extension state is handled correctly
# across multiple sessions. Specifically, if the extension state
# changes in one session (e.g., the extension is created or dropped),
# this should be reflected in other concurrent sessions.
#
# To test this, we start one interactive "background" psql session
# that stays open for the duration of the tests and then change the
# extension state from other sessions.
my $node = TimescaleNode->create('extension');
my $in = '';
my $out = '';
my $timer = IPC::Run::timeout(180);
my $h = $node->interactive_psql('postgres', \$in, \$out, $timer, on_error_stop => 0);

sub check_extension_state
{
my ($pattern, $annotation) = @_;

# report test failures from caller location
local $Test::Builder::Level = $Test::Builder::Level + 1;

# reset output collector
$out = "";
# restart per-command timer
$timer->start(5);
# send the data to be sent
$in .= "SELECT * FROM extension_state();\n";
# wait ...

pump $h until ($out =~ $pattern || $timer->is_expired);
my $okay = ($out =~ $pattern && !$timer->is_expired);
ok($okay, $annotation);
# for debugging, log actual output if it didn't match
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Useqq = 1;
diag 'Actual output was ' . Dumper($out) . "Did not match \"$pattern\"\n"
if !$okay;

# Read the next command prompt to prepare for next command
$out = "";
pump $h until ($out =~ qr/postgres=# / || $timer->is_expired);
return;
}

# Create extension_state function and check initial state
my $result = $node->safe_psql('postgres',
q{
\ir t/functions.sql
SELECT * FROM extension_state();
});

# Initially, the state should be "created" in both sessions
is($result, qq/created/, "initial state is \"created\"");
check_extension_state(qr/created/, "initial state is \"created\"");

# Drop the extension in one session, and the new state should be
# reflected in the other backend.
$result = $node->safe_psql('postgres', q{
DROP EXTENSION timescaledb;
SELECT * FROM extension_state();
});

# After dropping the extension, the new state in both sessions should
# be "unknown"
is($result, qq/unknown/, "state after dropped is \"unknown\"");
check_extension_state(qr/unknown/, "state is \"unknown\" after extension is dropped in other backend");

# Create the extension again, which should be reflected in both
# sessions.
$result = $node->safe_psql('postgres', q{
CREATE EXTENSION timescaledb;
SELECT * FROM extension_state();
});

# After creating the extension again in one session, the other session
# should go back to "created" state.
is($result, qq/created/, "state after creating extension is \"created\"");
check_extension_state(qr/created/, "state is \"created\" after extension is created in other backend");

# Quit the interactive psql session
$in .= q{
\q
};

$h->finish or die "psql returned $?";
$node->stop;
12 changes: 12 additions & 0 deletions test/t/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
set(PROVE_TEST_FILES 001_extension.pl)

if(CMAKE_BUILD_TYPE MATCHES Debug)
list(APPEND PROVE_TEST_FILES ${PROVE_DEBUG_TEST_FILES})
endif(CMAKE_BUILD_TYPE MATCHES Debug)

foreach(P_FILE ${PROVE_TEST_FILES})
configure_file(${P_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${P_FILE} COPYONLY)
endforeach(P_FILE)

set(MODULE_PATHNAME "$libdir/timescaledb-${PROJECT_VERSION_MOD}")
configure_file(functions.sql.in functions.sql)
2 changes: 2 additions & 0 deletions test/t/functions.sql.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CREATE FUNCTION extension_state() RETURNS TEXT
AS '@MODULE_PATHNAME@', 'ts_extension_get_state' LANGUAGE C;
4 changes: 2 additions & 2 deletions tsl/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ endif()

if(TAP_CHECKS)
add_custom_target(
provecheck
provecheck-t
COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/tmp_check
COMMAND
CONFDIR=${CMAKE_BINARY_DIR}/tsl/test PATH="${PG_BINDIR}:$ENV{PATH}"
PG_REGRESS=${PG_REGRESS} SRC_DIR=${PG_SOURCE_DIR}
CM_SRC_DIR=${CMAKE_SOURCE_DIR} PG_LIBDIR=${PG_LIBDIR}
${PRIMARY_TEST_DIR}/pg_prove.sh
USES_TERMINAL)
list(APPEND _install_checks provecheck)
list(APPEND _install_checks provecheck-t)
elseif(REQUIRE_ALL_TESTS)
message(
FATAL_ERROR
Expand Down

0 comments on commit 6cddc41

Please sign in to comment.