diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index bc3a8dd6..1bac20ea 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -221,6 +221,8 @@ jobs:
sleep 60
+ R -q -e "library(opalr); opal <- opal.login('administrator', 'datashield_test&', url='http://localhost:8080/'); dsadmin.profile_init(opal, name = 'default', packages = c('dsBase', 'dsTidyverse', 'resourcer')); opal.logout(opal)"
+
R -q -e "library(opalr); opal <- opal.login('administrator','datashield_test&', url='http://localhost:8080/'); dsadmin.set_option(opal, 'default.datashield.privacyControlLevel', 'permissive'); opal.logout(opal)"
workingDirectory: $(Pipeline.Workspace)/dsBaseClient/tests/testthat/data_files
diff --git a/opal_azure-pipelines.yml b/opal_azure-pipelines.yml
index bc3a8dd6..1bac20ea 100644
--- a/opal_azure-pipelines.yml
+++ b/opal_azure-pipelines.yml
@@ -221,6 +221,8 @@ jobs:
sleep 60
+ R -q -e "library(opalr); opal <- opal.login('administrator', 'datashield_test&', url='http://localhost:8080/'); dsadmin.profile_init(opal, name = 'default', packages = c('dsBase', 'dsTidyverse', 'resourcer')); opal.logout(opal)"
+
R -q -e "library(opalr); opal <- opal.login('administrator','datashield_test&', url='http://localhost:8080/'); dsadmin.set_option(opal, 'default.datashield.privacyControlLevel', 'permissive'); opal.logout(opal)"
workingDirectory: $(Pipeline.Workspace)/dsBaseClient/tests/testthat/data_files
diff --git a/tests/testthat/perf_files/armadillo_azure-pipeline_perf-profile.csv b/tests/testthat/perf_files/armadillo_azure-pipeline_perf-profile.csv
index ca8a229c..1fb2dce2 100644
--- a/tests/testthat/perf_files/armadillo_azure-pipeline_perf-profile.csv
+++ b/tests/testthat/perf_files/armadillo_azure-pipeline_perf-profile.csv
@@ -1,14 +1,29 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
-"conndisconn::perf::simple0","0.1661","0.5","2"
-"ds.abs::perf::0","6.247","0.5","2"
-"ds.asInteger::perf:0","5.691","0.5","2"
-"ds.asList::perf:0","12.50","0.5","2"
-"ds.asNumeric::perf:0","5.723","0.5","2"
-"ds.assign::perf::0","10.52","0.5","2"
-"ds.class::perf::combine:0","13.73","0.5","2"
-"ds.colnames::perf:0","25.64","0.5","2"
-"ds.exists::perf::combine:0","25.73","0.5","2"
-"ds.length::perf::combine:0","25.44","0.5","2"
-"ds.mean::perf::combine:0","25.70","0.5","2"
-"ds.mean::perf::split:0","25.72","0.5","2"
-"void::perf::void::0","53016.0","0.5","2"
+"conndisconn::perf::simple0","0.1621","0.5","2"
+"ds.abs::perf::0","5.989","0.5","2"
+"ds.asCharacter::perf::0","5.413","0.5","2"
+"ds.asDataMatrix::perf::0","5.466","0.5","2"
+"ds.asInteger::perf:0","5.570","0.5","2"
+"ds.asList::perf:0","11.72","0.5","2"
+"ds.asLogical::perf::0","5.516","0.5","2"
+"ds.asMatrix::perf::0","5.507","0.5","2"
+"ds.asNumeric::perf:0","5.552","0.5","2"
+"ds.assign::perf::0","10.21","0.5","2"
+"ds.class::perf::combine:0","12.30","0.5","2"
+"ds.colnames::perf:0","24.39","0.5","2"
+"ds.completeCases::perf::combine:0","5.521","0.5","2"
+"ds.dim::perf::combine:0","24.63","0.5","2"
+"ds.exists::perf::combine:0","24.74","0.5","2"
+"ds.exp::perf::0","6.218","0.5","2"
+"ds.isNA::perf::combine:0","9.480","0.5","2"
+"ds.length::perf::combine:0","24.94","0.5","2"
+"ds.levels::perf::combine:0","9.551","0.5","2"
+"ds.log::perf::0","6.286","0.5","2"
+"ds.ls::perf::combine:0","25.05","0.5","2"
+"ds.mean::perf::combine:0","24.63","0.5","2"
+"ds.mean::perf::split:0","25.20","0.5","2"
+"ds.names::perf::combine:0","12.37","0.5","2"
+"ds.numNA::perf::combine:0","9.64","0.5","2"
+"ds.sqrt::perf::0","6.218","0.5","2"
+"ds.unique::perf::combine:0","10.20","0.5","2"
+"void::perf::void::0","51340.0","0.5","2"
diff --git a/tests/testthat/perf_files/armadillo_hp-laptop-quay_perf-profile.csv b/tests/testthat/perf_files/armadillo_hp-laptop-quay_perf-profile.csv
index ebfd605c..c31bd3a8 100644
--- a/tests/testthat/perf_files/armadillo_hp-laptop-quay_perf-profile.csv
+++ b/tests/testthat/perf_files/armadillo_hp-laptop-quay_perf-profile.csv
@@ -1,14 +1,29 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
-"conndisconn::perf::simple0","0.06790","0.5","2"
-"ds.abs::perf::0","1.998","0.5","2"
-"ds.asInteger::perf:0","2.111","0.5","2"
-"ds.asList::perf:0","4.550","0.5","2"
-"ds.asNumeric::perf:0","1.879","0.5","2"
-"ds.assign::perf::0","4.806","0.5","2"
-"ds.class::perf::combine:0","3.865","0.5","2"
-"ds.colnames::perf:0","9.577","0.5","2"
-"ds.exists::perf::combine:0","8.411","0.5","2"
-"ds.length::perf::combine:0","7.957","0.5","2"
-"ds.mean::perf::combine:0","9.049","0.5","2"
-"ds.mean::perf::split:0","9.429","0.5","2"
-"void::perf::void::0","21400.0","0.5","2"
+"conndisconn::perf::simple0","0.07683","0.5","2"
+"ds.abs::perf::0","2.473","0.5","2"
+"ds.asCharacter::perf::0","2.295","0.5","2"
+"ds.asDataMatrix::perf::0","1.999","0.5","2"
+"ds.asInteger::perf:0","1.148","0.5","2"
+"ds.asList::perf:0","3.398","0.5","2"
+"ds.asLogical::perf::0","1.660","0.5","2"
+"ds.asMatrix::perf::0","1.835","0.5","2"
+"ds.asNumeric::perf:0","1.632","0.5","2"
+"ds.assign::perf::0","3.487","0.5","2"
+"ds.class::perf::combine:0","5.037","0.5","2"
+"ds.colnames::perf:0","8.219","0.5","2"
+"ds.completeCases::perf::combine:0","1.896","0.5","2"
+"ds.dim::perf::combine:0","8.337","0.5","2"
+"ds.exists::perf::combine:0","9.133","0.5","2"
+"ds.exp::perf::0","2.317","0.5","2"
+"ds.isNA::perf::combine:0","3.035","0.5","2"
+"ds.length::perf::combine:0","9.551","0.5","2"
+"ds.levels::perf::combine:0","3.275","0.5","2"
+"ds.log::perf::0","2.297","0.5","2"
+"ds.ls::perf::combine:0","8.168","0.5","2"
+"ds.mean::perf::combine:0","8.168","0.5","2"
+"ds.mean::perf::split:0","8.465","0.5","2"
+"ds.names::perf::combine:0","4.190","0.5","2"
+"ds.numNA::perf::combine:0","3.588","0.5","2"
+"ds.sqrt::perf::0","2.278","0.5","2"
+"ds.unique::perf::combine:0","3.909","0.5","2"
+"void::perf::void::0","20280.0","0.5","2"
diff --git a/tests/testthat/perf_files/opal_default_perf-profile.csv b/tests/testthat/perf_files/opal_default_perf-profile.csv
deleted file mode 100644
index 58614204..00000000
--- a/tests/testthat/perf_files/opal_default_perf-profile.csv
+++ /dev/null
@@ -1,14 +0,0 @@
-"refer_name","rate","lower_tolerance","upper_tolerance"
-"conndisconn::perf::simple0","0.2725","0.5","2"
-"ds.abs::perf::0","2.677","0.5","2"
-"ds.asInteger::perf:0","2.294","0.5","2"
-"ds.asList::perf:0","4.587","0.5","2"
-"ds.asNumeric::perf:0","2.185","0.5","2"
-"ds.assign::perf::0","5.490","0.5","2"
-"ds.class::perf::combine:0","4.760","0.5","2"
-"ds.colnames::perf:0","9.079","0.5","2"
-"ds.exists::perf::combine:0","11.09","0.5","2"
-"ds.length::perf::combine:0","9.479","0.5","2"
-"ds.mean::perf::combine:0","9.650","0.5","2"
-"ds.mean::perf::split:0","11.26","0.5","2"
-"void::perf::void::0","46250.0","0.5","2"
diff --git a/tests/testthat/perf_files/opal_hp-laptop-quay_perf-profile.csv b/tests/testthat/perf_files/opal_hp-laptop-quay_perf-profile.csv
index 563b84ec..44c87949 100644
--- a/tests/testthat/perf_files/opal_hp-laptop-quay_perf-profile.csv
+++ b/tests/testthat/perf_files/opal_hp-laptop-quay_perf-profile.csv
@@ -1,14 +1,29 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
-"conndisconn::perf::simple0","0.147643461923159","0.5","2"
-"ds.abs::perf::0","0.631818039001181","0.5","2"
-"ds.asInteger::perf:0","0.675696161933654","0.5","2"
-"ds.asList::perf:0","1.59078428438764","0.5","2"
-"ds.asNumeric::perf:0","0.692813012683229","0.5","2"
-"ds.assign::perf::0","1.89351857736982","0.5","2"
-"ds.class::perf::combine:0","1.62870246867488","0.5","2"
-"ds.colnames::perf:0","2.7125071","0.5","2"
-"ds.exists::perf::combine:0","3.45004426293124","0.5","2"
-"ds.length::perf::combine:0","2.78832377100152","0.5","2"
-"ds.mean::perf::combine:0","2.7801284055162","0.5","2"
-"ds.mean::perf::split:0","3.67443474363821","0.5","2"
-"void::perf::void::0","18974.1385397392","0.5","2"
+"conndisconn::perf::simple0","0.1267","0.5","2"
+"ds.abs::perf::0","0.7542","0.5","2"
+"ds.asCharacter::perf::0","0.8126","0.5","2"
+"ds.asDataMatrix::perf::0","0.8100","0.5","2"
+"ds.asInteger::perf:0","0.8600","0.5","2"
+"ds.asList::perf:0","","0.5","2"
+"ds.asLogical::perf::0","1.660","0.5","2"
+"ds.asMatrix::perf::0","0.8557","0.5","2"
+"ds.asNumeric::perf:0","0.7912","0.5","2"
+"ds.assign::perf::0","2.003","0.5","2"
+"ds.class::perf::combine:0","1.645","0.5","2"
+"ds.colnames::perf:0","3.392","0.5","2"
+"ds.completeCases::perf::combine:0","0.8218","0.5","2"
+"ds.dim::perf::combine:0","3.265","0.5","2"
+"ds.exists::perf::combine:0","4.042","0.5","2"
+"ds.exp::perf::0","0.8963","0.5","2"
+"ds.isNA::perf::combine:0","1.297","0.5","2"
+"ds.length::perf::combine:0","3.212","0.5","2"
+"ds.levels::perf::combine:0","1.326","0.5","2"
+"ds.log::perf::0","0.9260","0.5","2"
+"ds.ls::perf::combine:0","3.282","0.5","2"
+"ds.mean::perf::combine:0","3.281","0.5","2"
+"ds.mean::perf::split:0","4.029","0.5","2"
+"ds.names::perf::combine:0","1.713","0.5","2"
+"ds.numNA::perf::combine:0","1.274","0.5","2"
+"ds.sqrt::perf::0","0.9233","0.5","2"
+"ds.unique::perf::combine:0","1.628","0.5","2"
+"void::perf::void::0","23600","0.5","2"
diff --git a/tests/testthat/test-perf-ds.asCharacter.R b/tests/testthat/test-perf-ds.asCharacter.R
new file mode 100644
index 00000000..e258851e
--- /dev/null
+++ b/tests/testthat/test-perf-ds.asCharacter.R
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.asCharacter::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.asCharacter::perf:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.asCharacter("D$LAB_TSC", newobj = "perf.newobj")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.asCharacter::perf::0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.asCharacter::perf::0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.asCharacter::perf::0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.asCharacter::perf::0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.asCharacter::perf::0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.asCharacter::perf::0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.asCharacter::perf::0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.asCharacter::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.asCharacter::perf::done")
diff --git a/tests/testthat/test-perf-ds.asDataMatrix.R b/tests/testthat/test-perf-ds.asDataMatrix.R
new file mode 100644
index 00000000..87555038
--- /dev/null
+++ b/tests/testthat/test-perf-ds.asDataMatrix.R
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.asDataMatrix::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.asDataMatrix::perf:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.asDataMatrix(x.name = "D", newobj = "perf.newobj")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.asDataMatrix::perf::0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.asDataMatrix::perf::0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.asDataMatrix::perf::0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.asDataMatrix::perf::0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.asDataMatrix::perf::0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.asDataMatrix::perf::0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.asDataMatrix::perf::0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.asDataMatrix::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.asDataMatrix::perf::done")
diff --git a/tests/testthat/test-perf-ds.asLogical.R b/tests/testthat/test-perf-ds.asLogical.R
new file mode 100644
index 00000000..edfb7997
--- /dev/null
+++ b/tests/testthat/test-perf-ds.asLogical.R
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.asLogical::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.asLogical::perf:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.asLogical("D$LAB_TSC", newobj = "perf.newobj")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.asLogical::perf::0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.asLogical::perf::0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.asLogical::perf::0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.asLogical::perf::0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.asLogical::perf::0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.asLogical::perf::0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.asLogical::perf::0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.asLogical::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.asLogical::perf::done")
diff --git a/tests/testthat/test-perf-ds.asMatrix.R b/tests/testthat/test-perf-ds.asMatrix.R
new file mode 100644
index 00000000..68624a75
--- /dev/null
+++ b/tests/testthat/test-perf-ds.asMatrix.R
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.asMatrix::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.asMatrix::perf:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.asMatrix(x.name = "D$LAB_TSC", newobj = "perf.newobj")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.asMatrix::perf::0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.asMatrix::perf::0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.asMatrix::perf::0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.asMatrix::perf::0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.asMatrix::perf::0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.asMatrix::perf::0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.asMatrix::perf::0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.asMatrix::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.asMatrix::perf::done")
diff --git a/tests/testthat/test-perf-ds.completeCases.R b/tests/testthat/test-perf-ds.completeCases.R
new file mode 100644
index 00000000..fcfc5ba2
--- /dev/null
+++ b/tests/testthat/test-perf-ds.completeCases.R
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024-2025 Arjuna Technologies, Newcastle upon Tyne. All rights reserved.
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.completeCases::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.completeCases::perf::combine:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.completeCases("D", newobj="D_complete")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.completeCases::perf::combine:0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.completeCases::perf::combine:0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.completeCases::perf::combine:0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.completeCases::perf::combine:0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.completeCases::perf::combine:0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.completeCases::perf::combine:0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.completeCases::perf::combine:0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.completeCases::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.completeCases::perf::done")
diff --git a/tests/testthat/test-perf-ds.dim.R b/tests/testthat/test-perf-ds.dim.R
new file mode 100644
index 00000000..917eb1a1
--- /dev/null
+++ b/tests/testthat/test-perf-ds.dim.R
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024-2025 Arjuna Technologies, Newcastle upon Tyne. All rights reserved.
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.dim::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.dim::perf::combine:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.dim("D")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.dim::perf::combine:0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.dim::perf::combine:0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.dim::perf::combine:0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.dim::perf::combine:0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.dim::perf::combine:0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.dim::perf::combine:0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.dim::perf::combine:0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.dim::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.dim::perf::done")
diff --git a/tests/testthat/test-perf-ds.exp.R b/tests/testthat/test-perf-ds.exp.R
new file mode 100644
index 00000000..293223b4
--- /dev/null
+++ b/tests/testthat/test-perf-ds.exp.R
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.exp::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.exp::perf:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.exp("D$LAB_TSC", newobj = "perf.newobj")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.exp::perf::0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.exp::perf::0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.exp::perf::0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.exp::perf::0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.exp::perf::0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.exp::perf::0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.exp::perf::0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.exp::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.exp::perf::done")
diff --git a/tests/testthat/test-perf-ds.isNA.R b/tests/testthat/test-perf-ds.isNA.R
new file mode 100644
index 00000000..7cbb6201
--- /dev/null
+++ b/tests/testthat/test-perf-ds.isNA.R
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024-2025 Arjuna Technologies, Newcastle upon Tyne. All rights reserved.
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.isNA::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.isNA::perf::combine:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.isNA("D$LAB_TSC")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.isNA::perf::combine:0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.isNA::perf::combine:0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.isNA::perf::combine:0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.isNA::perf::combine:0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.isNA::perf::combine:0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.isNA::perf::combine:0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.isNA::perf::combine:0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.isNA::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.isNA::perf::done")
diff --git a/tests/testthat/test-perf-ds.levels.R b/tests/testthat/test-perf-ds.levels.R
new file mode 100644
index 00000000..688458d1
--- /dev/null
+++ b/tests/testthat/test-perf-ds.levels.R
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024-2025 Arjuna Technologies, Newcastle upon Tyne. All rights reserved.
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.levels::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "PM_BMI_CATEGORICAL"))
+
+#
+# Tests
+#
+
+# context("ds.levels::perf::combine:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.levels("D$PM_BMI_CATEGORICAL")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.levels::perf::combine:0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.levels::perf::combine:0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.levels::perf::combine:0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.levels::perf::combine:0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.levels::perf::combine:0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.levels::perf::combine:0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.levels::perf::combine:0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.levels::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.levels::perf::done")
diff --git a/tests/testthat/test-perf-ds.log.R b/tests/testthat/test-perf-ds.log.R
new file mode 100644
index 00000000..4f655960
--- /dev/null
+++ b/tests/testthat/test-perf-ds.log.R
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.log::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.log::perf:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.log("D$LAB_TSC", newobj = "perf.newobj")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.log::perf::0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.log::perf::0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.log::perf::0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.log::perf::0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.log::perf::0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.log::perf::0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.log::perf::0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.log::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.log::perf::done")
diff --git a/tests/testthat/test-perf-ds.ls.R b/tests/testthat/test-perf-ds.ls.R
new file mode 100644
index 00000000..5e428cd6
--- /dev/null
+++ b/tests/testthat/test-perf-ds.ls.R
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024-2025 Arjuna Technologies, Newcastle upon Tyne. All rights reserved.
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.ls::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.ls::perf::combine:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.ls()
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.ls::perf::combine:0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.ls::perf::combine:0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.ls::perf::combine:0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.ls::perf::combine:0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.ls::perf::combine:0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.ls::perf::combine:0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.ls::perf::combine:0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.ls::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.ls::perf::done")
diff --git a/tests/testthat/test-perf-ds.names.R b/tests/testthat/test-perf-ds.names.R
new file mode 100644
index 00000000..653497c1
--- /dev/null
+++ b/tests/testthat/test-perf-ds.names.R
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024-2025 Arjuna Technologies, Newcastle upon Tyne. All rights reserved.
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.names::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.names::perf::combine:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.names("D")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.names::perf::combine:0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.names::perf::combine:0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.names::perf::combine:0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.names::perf::combine:0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.names::perf::combine:0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.names::perf::combine:0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.names::perf::combine:0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.names::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.names::perf::done")
diff --git a/tests/testthat/test-perf-ds.numNA.R b/tests/testthat/test-perf-ds.numNA.R
new file mode 100644
index 00000000..2e6ae3e4
--- /dev/null
+++ b/tests/testthat/test-perf-ds.numNA.R
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024-2025 Arjuna Technologies, Newcastle upon Tyne. All rights reserved.
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.numNA::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.numNA::perf::combine:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.numNA("D$LAB_TSC")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.numNA::perf::combine:0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.numNA::perf::combine:0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.numNA::perf::combine:0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.numNA::perf::combine:0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.numNA::perf::combine:0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.numNA::perf::combine:0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.numNA::perf::combine:0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.numNA::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.numNA::perf::done")
diff --git a/tests/testthat/test-perf-ds.sqrt.R b/tests/testthat/test-perf-ds.sqrt.R
new file mode 100644
index 00000000..ae96dde7
--- /dev/null
+++ b/tests/testthat/test-perf-ds.sqrt.R
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.sqrt::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.sqrt::perf:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.sqrt("D$LAB_TSC", newobj = "perf.newobj")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.sqrt::perf::0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.sqrt::perf::0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.sqrt::perf::0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.sqrt::perf::0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.sqrt::perf::0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.sqrt::perf::0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.sqrt::perf::0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.sqrt::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.sqrt::perf::done")
diff --git a/tests/testthat/test-perf-ds.unique.R b/tests/testthat/test-perf-ds.unique.R
new file mode 100644
index 00000000..093c5267
--- /dev/null
+++ b/tests/testthat/test-perf-ds.unique.R
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024-2025 Arjuna Technologies, Newcastle upon Tyne. All rights reserved.
+#
+# This program and the accompanying materials
+# are made available under the terms of the GNU Public License v3.0.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#-------------------------------------------------------------------------------
+
+#
+# Set up
+#
+
+# context("ds.unique::perf::setup")
+connect.studies.dataset.cnsim(list("LAB_TSC", "LAB_TRIG"))
+
+#
+# Tests
+#
+
+# context("ds.unique::perf::combine:0")
+test_that("combine - performance", {
+ .durationSec <- perf.testduration(30)
+ .count <- 0
+ .start.time <- Sys.time()
+ .current.time <- .start.time
+
+ while (difftime(.current.time, .start.time, units = "secs")[[1]] < .durationSec) {
+ ds.unique("D$LAB_TSC", newobj="unique_TSC")
+
+ .count <- .count + 1
+ .current.time <- Sys.time()
+ }
+
+ .current.rate <- .count / (difftime(.current.time, .start.time, units = "secs")[[1]])
+ .reference.rate <- perf.reference.rate("ds.unique::perf::combine:0")
+ if (any(length(.reference.rate) == 0) || any(is.null(.reference.rate))) {
+ print(paste("ds.unique::perf::combine:0 ", .current.rate, 0.5, 2.0))
+ perf.reference.save("ds.unique::perf::combine:0", .current.rate, 0.5, 2.0)
+ } else {
+ print(paste("ds.unique::perf::combine:0 ", format(.current.rate, digits = 8), ", ", format(100.0 * .current.rate / .reference.rate, digits = 4), "%", sep = ''))
+ }
+
+ .reference.rate <- perf.reference.rate("ds.unique::perf::combine:0")
+ .reference.tolerance.lower <- perf.reference.tolerance.lower("ds.unique::perf::combine:0")
+ .reference.tolerance.upper <- perf.reference.tolerance.upper("ds.unique::perf::combine:0")
+
+ expect_gt(.current.rate, .reference.rate * .reference.tolerance.lower, label = "Observed rate", expected.label = "lower threshold on rate")
+ expect_lt(.current.rate, .reference.rate * .reference.tolerance.upper, label = "Observed rate", expected.label = "upper threshold on rate")
+})
+
+#
+# Done
+#
+
+# context("ds.unique::perf::shutdown")
+disconnect.studies.dataset.cnsim()
+# context("ds.unique::perf::done")