diff options
author | 2023-08-04 14:29:55 +0100 | |
---|---|---|
committer | 2023-08-04 17:23:10 +0100 | |
commit | 0a3b420e82a28dd93199a04ae09ad686c5ab82eb (patch) | |
tree | 5a4650349273235517eb607b639efac4c69e2509 /integration-test/src/test | |
parent | 7a049060b9ee2306eb562f75a1cbd0d50eb14f41 (diff) |
Update performance test timings for larger dataset
- Populate 3 anchors with 1000 openroadm devices nodes for
read and query tests (over 250,000 fragments)
- Increase update perf tests to update 100 out of 1000 devices
- Increase delete perf tests to delete 100 out of 300 devices
- Remove bookstore data from performance tests
- Update test timings
Issue-ID: CPS-1811
Signed-off-by: danielhanrahan <daniel.hanrahan@est.tech>
Change-Id: Iaa36694be907278e772ae729a85510ea9d004fd1
Diffstat (limited to 'integration-test/src/test')
9 files changed, 159 insertions, 159 deletions
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy index 8a3bd6d23..1dbd71613 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy @@ -24,11 +24,13 @@ import org.onap.cps.rest.utils.MultipartFileUtil import org.onap.cps.spi.FetchDescendantsOption import org.springframework.web.multipart.MultipartFile +import java.util.concurrent.TimeUnit + class CpsPerfTestBase extends PerfTestBase { static final def CPS_PERFORMANCE_TEST_DATASPACE = 'cpsPerformanceDataspace' - static final def OPENROADM_ANCHORS = 5 - static final def OPENROADM_DEVICES_PER_ANCHOR = 50 + static final def OPENROADM_ANCHORS = 3 + static final def OPENROADM_DEVICES_PER_ANCHOR = 1000 static final def OPENROADM_DATANODES_PER_DEVICE = 86 def printTitle() { @@ -46,30 +48,10 @@ class CpsPerfTestBase extends PerfTestBase { } def createInitialData() { - createWarmupData() - createLargeBookstoresData() addOpenRoadModel() addOpenRoadData() } - def createWarmupData() { - def data = "{\"bookstore\":{}}" - stopWatch.start() - addAnchorsWithData(1, CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'warmup', data) - stopWatch.stop() - def durationInMillis = stopWatch.getTotalTimeMillis() - recordAndAssertPerformance('Creating warmup anchor with tiny data tree', 500, durationInMillis) - } - - def createLargeBookstoresData() { - def data = readResourceDataFile('bookstore/largeModelData.json') - stopWatch.start() - addAnchorsWithData(5, CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'bookstore', data) - stopWatch.stop() - def durationInMillis = stopWatch.getTotalTimeMillis() - recordAndAssertPerformance('Creating bookstore anchors with large data tree', 1_500, durationInMillis) - } - def addOpenRoadModel() { def file = new File('src/test/resources/data/openroadm/correctedModel.zip') def multipartFile = Mock(MultipartFile) @@ -84,7 +66,7 @@ class CpsPerfTestBase extends PerfTestBase { addAnchorsWithData(OPENROADM_ANCHORS, CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'openroadm', data) stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() - recordAndAssertPerformance('Creating openroadm anchors with large data tree', 20_000, durationInMillis) + recordAndAssertPerformance('Creating openroadm anchors with large data tree', TimeUnit.SECONDS.toMillis(200), durationInMillis) } def generateOpenRoadData(numberOfNodes) { @@ -95,14 +77,14 @@ class CpsPerfTestBase extends PerfTestBase { } def 'Warm the database'() { - when: 'get data nodes for warmup anchor' + when: 'dummy get data nodes runs so that populating the DB does not get included in other test timings' stopWatch.start() - def result = cpsDataService.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'warmup1', '/', FetchDescendantsOption.OMIT_DESCENDANTS) + def result = cpsDataService.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', '/', FetchDescendantsOption.OMIT_DESCENDANTS) assert countDataNodesInTree(result) == 1 stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() - then: 'all data is read within 20 seconds' - recordAndAssertPerformance("Warming database", 20_000, durationInMillis) + then: 'all data is read within expected time' + recordAndAssertPerformance("Warming database", 100, durationInMillis) } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/PerfTestBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/PerfTestBase.groovy index 4afdabe92..b6ceb91b5 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/PerfTestBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/PerfTestBase.groovy @@ -61,7 +61,7 @@ abstract class PerfTestBase extends CpsIntegrationSpecBase { if (shortTitle.length() > 40) { shortTitle = shortTitle.substring(0, 40) } - def record = String.format('%2d.%-40s limit%,7d took %,7d ms ', PERFORMANCE_RECORD.size() + 1, shortTitle, thresholdInMs, recordedTimeInMs) + def record = String.format('%2d.%-40s limit%,8d took %,8d ms ', PERFORMANCE_RECORD.size() + 1, shortTitle, thresholdInMs, recordedTimeInMs) record += pass ? 'PASS' : 'FAIL' PERFORMANCE_RECORD.add(record) assert recordedTimeInMs <= thresholdInMs diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimitsPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimitsPerfTest.groovy index 9cb65ab8f..659c9f579 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimitsPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimitsPerfTest.groovy @@ -24,6 +24,8 @@ import java.time.OffsetDateTime import org.onap.cps.api.CpsDataService import org.onap.cps.integration.performance.base.CpsPerfTestBase +import java.util.concurrent.TimeUnit + import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS @@ -46,8 +48,8 @@ class CpsDataServiceLimitsPerfTest extends CpsPerfTestBase { } stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() - then: 'the operation completes within 10 seconds' - recordAndAssertPerformance("Creating 33,000 books", 10_000, durationInMillis) + then: 'the operation completes within 25 seconds' + recordAndAssertPerformance("Creating 33,000 books", TimeUnit.SECONDS.toMillis(25), durationInMillis) } def 'Get data nodes from multiple xpaths 32K (2^15) limit exceeded.'() { @@ -87,8 +89,8 @@ class CpsDataServiceLimitsPerfTest extends CpsPerfTestBase { cpsAdminService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor') stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() - then: 'test data is deleted in 10 seconds' - recordAndAssertPerformance("Deleting test data", 10_000, durationInMillis) + then: 'test data is deleted in 1 second' + recordAndAssertPerformance("Deleting test data", TimeUnit.SECONDS.toMillis(1), durationInMillis) } def countDataNodes() { diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeletePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeletePerfTest.groovy index e80a87d50..fb836b1c3 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeletePerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeletePerfTest.groovy @@ -26,6 +26,8 @@ import java.time.OffsetDateTime import org.onap.cps.api.CpsDataService import org.onap.cps.integration.performance.base.CpsPerfTestBase +import java.util.concurrent.TimeUnit + class DeletePerfTest extends CpsPerfTestBase { CpsDataService objectUnderTest @@ -35,30 +37,33 @@ class DeletePerfTest extends CpsPerfTestBase { def 'Create test data (please note, subsequent tests depend on this running first).'() { when: 'multiple anchors with a node with a large number of descendants is created' stopWatch.start() - def data = generateOpenRoadData(50) + def data = generateOpenRoadData(300) addAnchorsWithData(10, CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'delete', data) stopWatch.stop() def setupDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'setup duration is under 40 seconds' - recordAndAssertPerformance('Delete test setup', 40_000, setupDurationInMillis) + then: 'setup duration is within expected time' + recordAndAssertPerformance('Delete test setup', TimeUnit.SECONDS.toMillis(200), setupDurationInMillis) } - def 'Delete 10 container nodes'() { + def 'Delete 100 container nodes'() { + given: 'a list of xpaths to delete' + def xpathsToDelete = (1..100).collect { + "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']/org-openroadm-device" + } when: 'child nodes are deleted' stopWatch.start() - (1..10).each { - def childPath = "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']/org-openroadm-device" - objectUnderTest.deleteDataNode(CPS_PERFORMANCE_TEST_DATASPACE, 'delete1', childPath, OffsetDateTime.now()) + xpathsToDelete.each { + objectUnderTest.deleteDataNode(CPS_PERFORMANCE_TEST_DATASPACE, 'delete1', it, OffsetDateTime.now()) } stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Delete 10 containers', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Delete 100 containers', TimeUnit.SECONDS.toMillis(2), deleteDurationInMillis) } - def 'Batch delete 50 container nodes'() { + def 'Batch delete 100 container nodes'() { given: 'a list of xpaths to delete' - def xpathsToDelete = (1..50).collect { + def xpathsToDelete = (1..100).collect { "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']/org-openroadm-device" } when: 'child nodes are deleted' @@ -66,56 +71,59 @@ class DeletePerfTest extends CpsPerfTestBase { objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'delete2', xpathsToDelete, OffsetDateTime.now()) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Batch delete 50 containers', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Batch delete 100 containers', 500, deleteDurationInMillis) } - def 'Delete 20 list elements'() { + def 'Delete 100 list elements'() { + given: 'a list of xpaths to delete' + def xpathsToDelete = (1..100).collect { + "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']" + } when: 'list elements are deleted' stopWatch.start() - (1..20).each { - def listElementXpath = "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-1']/org-openroadm-device/degree[@degree-number=" + it + "]" - objectUnderTest.deleteDataNode(CPS_PERFORMANCE_TEST_DATASPACE, 'delete3', listElementXpath, OffsetDateTime.now()) + xpathsToDelete.each { + objectUnderTest.deleteDataNode(CPS_PERFORMANCE_TEST_DATASPACE, 'delete3', it, OffsetDateTime.now()) } stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Delete 20 lists elements', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Delete 100 lists elements', TimeUnit.SECONDS.toMillis(2), deleteDurationInMillis) } - def 'Batch delete 1000 list elements'() { + def 'Batch delete 100 list elements'() { given: 'a list of xpaths to delete' - def xpathsToDelete = [] - for (int childIndex = 1; childIndex <= 50; childIndex++) { - xpathsToDelete.addAll((1..20).collect { - "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-${childIndex}']/org-openroadm-device/degree[@degree-number=${it}]".toString() - }) + def xpathsToDelete = (1..100).collect { + "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']" } when: 'list elements are deleted' stopWatch.start() objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'delete4', xpathsToDelete, OffsetDateTime.now()) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Batch delete 1000 lists elements', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Batch delete 100 lists elements', 500, deleteDurationInMillis) } - def 'Delete 10 whole lists'() { + def 'Delete 100 whole lists'() { + given: 'a list of xpaths to delete' + def xpathsToDelete = (1..100).collect { + "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']/org-openroadm-device/degree" + } when: 'lists are deleted' stopWatch.start() - (1..10).each { - def childPath = "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']/org-openroadm-device/degree" - objectUnderTest.deleteDataNode(CPS_PERFORMANCE_TEST_DATASPACE, 'delete5', childPath, OffsetDateTime.now()) + xpathsToDelete.each { + objectUnderTest.deleteDataNode(CPS_PERFORMANCE_TEST_DATASPACE, 'delete5', it, OffsetDateTime.now()) } stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Delete 10 whole lists', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Delete 100 whole lists', TimeUnit.SECONDS.toMillis(5), deleteDurationInMillis) } - def 'Batch delete 30 whole lists'() { + def 'Batch delete 100 whole lists'() { given: 'a list of xpaths to delete' - def xpathsToDelete = (1..30).collect { + def xpathsToDelete = (1..100).collect { "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']/org-openroadm-device/degree" } when: 'lists are deleted' @@ -123,8 +131,8 @@ class DeletePerfTest extends CpsPerfTestBase { objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'delete6', xpathsToDelete, OffsetDateTime.now()) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Batch delete 30 whole lists', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Batch delete 100 whole lists', TimeUnit.SECONDS.toMillis(4), deleteDurationInMillis) } def 'Delete 1 large data node'() { @@ -133,8 +141,8 @@ class DeletePerfTest extends CpsPerfTestBase { objectUnderTest.deleteDataNode(CPS_PERFORMANCE_TEST_DATASPACE, 'delete7', '/openroadm-devices', OffsetDateTime.now()) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Delete one large node', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Delete one large node', TimeUnit.SECONDS.toMillis(2), deleteDurationInMillis) } def 'Delete root node with many descendants'() { @@ -143,8 +151,8 @@ class DeletePerfTest extends CpsPerfTestBase { objectUnderTest.deleteDataNode(CPS_PERFORMANCE_TEST_DATASPACE, 'delete8', '/', OffsetDateTime.now()) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Delete root node', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Delete root node', TimeUnit.SECONDS.toMillis(2), deleteDurationInMillis) } def 'Delete data nodes for an anchor'() { @@ -153,8 +161,8 @@ class DeletePerfTest extends CpsPerfTestBase { objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'delete9', OffsetDateTime.now()) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Delete data nodes for anchor', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Delete data nodes for anchor', TimeUnit.SECONDS.toMillis(2), deleteDurationInMillis) } def 'Batch delete 100 non-existing nodes'() { @@ -167,8 +175,8 @@ class DeletePerfTest extends CpsPerfTestBase { } catch (DataNodeNotFoundException ignored) {} stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 300 milliseconds' - recordAndAssertPerformance('Batch delete 100 non-existing', 300, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Batch delete 100 non-existing', TimeUnit.SECONDS.toMillis(6), deleteDurationInMillis) } def 'Clean up test data'() { @@ -179,8 +187,8 @@ class DeletePerfTest extends CpsPerfTestBase { cpsAdminService.deleteAnchors(CPS_PERFORMANCE_TEST_DATASPACE, anchorNames) stopWatch.stop() def deleteDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'delete duration is under 1000 milliseconds' - recordAndAssertPerformance('Delete test cleanup', 1000, deleteDurationInMillis) + then: 'delete duration is within expected time' + recordAndAssertPerformance('Delete test cleanup', TimeUnit.SECONDS.toMillis(10), deleteDurationInMillis) } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy index a11dc3568..048b3b420 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy @@ -23,6 +23,8 @@ package org.onap.cps.integration.performance.cps import org.onap.cps.api.CpsDataService import org.onap.cps.integration.performance.base.CpsPerfTestBase +import java.util.concurrent.TimeUnit + import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS @@ -36,17 +38,17 @@ class GetPerfTest extends CpsPerfTestBase { def 'Read top-level node with #scenario.'() { when: 'get data nodes from 1 anchor' stopWatch.start() - def result = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchor, '/openroadm-devices', fetchDescendantsOption) + def result = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', '/openroadm-devices', fetchDescendantsOption) stopWatch.stop() assert countDataNodesInTree(result) == expectedNumberOfDataNodes def durationInMillis = stopWatch.getTotalTimeMillis() then: 'all data is read within #durationLimit ms' recordAndAssertPerformance("Read datatrees with ${scenario}", durationLimit, durationInMillis) where: 'the following parameters are used' - scenario | fetchDescendantsOption | anchor || durationLimit | expectedNumberOfDataNodes - 'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 50 | 1 - 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 100 | 1 + OPENROADM_DEVICES_PER_ANCHOR - 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 200 | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + scenario | fetchDescendantsOption || durationLimit | expectedNumberOfDataNodes + 'no descendants' | OMIT_DESCENDANTS || 10 | 1 + 'direct descendants' | DIRECT_CHILDREN_ONLY || 50 | 1 + OPENROADM_DEVICES_PER_ANCHOR + 'all descendants' | INCLUDE_ALL_DESCENDANTS || TimeUnit.SECONDS.toMillis(2) | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE } def 'Read data trees for multiple xpaths'() { @@ -54,13 +56,13 @@ class GetPerfTest extends CpsPerfTestBase { def xpaths = (1..OPENROADM_DEVICES_PER_ANCHOR).collect { "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']" } when: 'get data nodes from 1 anchor' stopWatch.start() - def result = objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm4', xpaths, INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', xpaths, INCLUDE_ALL_DESCENDANTS) stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() then: 'requested nodes and their descendants are returned' assert countDataNodesInTree(result) == OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE - and: 'all data is read within 200 ms' - recordAndAssertPerformance("Read datatrees for multiple xpaths", 200, durationInMillis) + and: 'all data is read within expected time' + recordAndAssertPerformance("Read datatrees for multiple xpaths", TimeUnit.SECONDS.toMillis(3) , durationInMillis) } def 'Read for multiple xpaths to non-existing datanodes'() { @@ -68,31 +70,29 @@ class GetPerfTest extends CpsPerfTestBase { def xpaths = (1..50).collect { "/path/to/non-existing/node[@id='" + it + "']" } when: 'get data nodes from 1 anchor' stopWatch.start() - def result = objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm4', xpaths, INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', xpaths, INCLUDE_ALL_DESCENDANTS) stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() then: 'no data is returned' assert result.isEmpty() - and: 'the operation completes within within 20 ms' - recordAndAssertPerformance("Read non-existing xpaths", 20, durationInMillis) + and: 'the operation completes within within expected time' + recordAndAssertPerformance("Read non-existing xpaths", 10, durationInMillis) } def 'Read complete data trees using #scenario.'() { - when: 'get data nodes for 5 anchors' + when: 'get data nodes from 1 anchor' stopWatch.start() - (1..5).each { - def result = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm' + it, xpath, INCLUDE_ALL_DESCENDANTS) - assert countDataNodesInTree(result) == expectedNumberOfDataNodes - } + def result = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm3', xpath, INCLUDE_ALL_DESCENDANTS) + assert countDataNodesInTree(result) == expectedNumberOfDataNodes stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() - then: 'all data is read within #durationLimit ms' + then: 'all data is read within expected time' recordAndAssertPerformance("Read datatrees using ${scenario}", durationLimit, durationInMillis) where: 'the following xpaths are used' - scenario | xpath || durationLimit | expectedNumberOfDataNodes - 'openroadm root' | '/' || 600 | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE - 'openroadm top element' | '/openroadm-devices' || 600 | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE - 'openroadm whole list' | '/openroadm-devices/openroadm-device' || 600 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + scenario | xpath || durationLimit | expectedNumberOfDataNodes + 'openroadm root' | '/' || TimeUnit.SECONDS.toMillis(2) | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 'openroadm top element' | '/openroadm-devices' || TimeUnit.SECONDS.toMillis(2) | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 'openroadm whole list' | '/openroadm-devices/openroadm-device' || TimeUnit.SECONDS.toMillis(3) | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy index afcc2eae2..01369181e 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy @@ -22,6 +22,9 @@ package org.onap.cps.integration.performance.cps import org.onap.cps.api.CpsQueryService import org.onap.cps.integration.performance.base.CpsPerfTestBase +import org.onap.cps.spi.PaginationOption + +import java.util.concurrent.TimeUnit import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS @@ -36,7 +39,7 @@ class QueryPerfTest extends CpsPerfTestBase { def 'Query complete data trees with #scenario.'() { when: 'query data nodes (using a fresh anchor with identical data for each test)' stopWatch.start() - def result = objectUnderTest.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchor, cpsPath, INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', cpsPath, INCLUDE_ALL_DESCENDANTS) stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() then: 'the expected number of nodes is returned' @@ -44,18 +47,18 @@ class QueryPerfTest extends CpsPerfTestBase { and: 'all data is read within #durationLimit ms' recordAndAssertPerformance("Query 1 anchor ${scenario}", durationLimit, durationInMillis) where: 'the following parameters are used' - scenario | anchor | cpsPath || durationLimit | expectedNumberOfDataNodes - 'top element' | 'openroadm1' | '/openroadm-devices' || 120 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1 - 'leaf condition' | 'openroadm2' | '//openroadm-device[@ne-state="inservice"]' || 200 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE - 'ancestors' | 'openroadm3' | '//openroadm-device/ancestor::openroadm-devices' || 120 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1 - 'leaf condition + ancestors' | 'openroadm4' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 120 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1 - 'non-existing data' | 'openroadm1' | '/path/to/non-existing/node[@id="1"]' || 10 | 0 + scenario | cpsPath || durationLimit | expectedNumberOfDataNodes + 'top element' | '/openroadm-devices' || TimeUnit.SECONDS.toMillis(2) | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1 + 'leaf condition' | '//openroadm-device[@ne-state="inservice"]' || TimeUnit.SECONDS.toMillis(3) | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 'ancestors' | '//openroadm-device/ancestor::openroadm-devices' || TimeUnit.SECONDS.toMillis(2) | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1 + 'leaf condition + ancestors' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || TimeUnit.SECONDS.toMillis(2) | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1 + 'non-existing data' | '/path/to/non-existing/node[@id="1"]' || 100 | 0 } def 'Query complete data trees across all anchors with #scenario.'() { when: 'query data nodes across all anchors' stopWatch.start() - def result = objectUnderTest.queryDataNodesAcrossAnchors('cpsPerformanceDataspace', cpspath, INCLUDE_ALL_DESCENDANTS) + def result = objectUnderTest.queryDataNodesAcrossAnchors(CPS_PERFORMANCE_TEST_DATASPACE, cpspath, INCLUDE_ALL_DESCENDANTS, PaginationOption.NO_PAGINATION) stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() then: 'the expected number of nodes is returned' @@ -63,17 +66,18 @@ class QueryPerfTest extends CpsPerfTestBase { and: 'all data is read within #durationLimit ms' recordAndAssertPerformance("Query across anchors ${scenario}", durationLimit, durationInMillis) where: 'the following parameters are used' - scenario | cpspath || durationLimit | expectedNumberOfDataNodes - 'top element' | '/openroadm-devices' || 400 | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1) - 'leaf condition' | '//openroadm-device[@ne-state="inservice"]' || 700 | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE) - 'ancestors' | '//openroadm-device/ancestor::openroadm-devices' || 400 | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1) - 'leaf condition + ancestors' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 400 | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1) + scenario | cpspath || durationLimit | expectedNumberOfDataNodes + 'top element' | '/openroadm-devices' || TimeUnit.SECONDS.toMillis(6) | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1) + 'leaf condition' | '//openroadm-device[@ne-state="inservice"]' || TimeUnit.SECONDS.toMillis(6) | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE) + 'ancestors' | '//openroadm-device/ancestor::openroadm-devices' || TimeUnit.SECONDS.toMillis(6) | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1) + 'leaf condition + ancestors' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || TimeUnit.SECONDS.toMillis(6) | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1) + 'non-existing data' | '/path/to/non-existing/node[@id="1"]' || 100 | 0 } def 'Query with leaf condition and #scenario.'() { when: 'query data nodes (using a fresh anchor with identical data for each test)' stopWatch.start() - def result = objectUnderTest.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchor, '//openroadm-device[@status="success"]', fetchDescendantsOption) + def result = objectUnderTest.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', '//openroadm-device[@status="success"]', fetchDescendantsOption) stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() then: 'the expected number of nodes is returned' @@ -81,16 +85,16 @@ class QueryPerfTest extends CpsPerfTestBase { and: 'all data is read within #durationLimit ms' recordAndAssertPerformance("Query with ${scenario}", durationLimit, durationInMillis) where: 'the following parameters are used' - scenario | fetchDescendantsOption | anchor || durationLimit | expectedNumberOfDataNodes - 'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 15 | OPENROADM_DEVICES_PER_ANCHOR - 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 60 | OPENROADM_DEVICES_PER_ANCHOR * 2 - 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 150 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + scenario | fetchDescendantsOption || durationLimit | expectedNumberOfDataNodes + 'no descendants' | OMIT_DESCENDANTS || 100 | OPENROADM_DEVICES_PER_ANCHOR + 'direct descendants' | DIRECT_CHILDREN_ONLY || 150 | OPENROADM_DEVICES_PER_ANCHOR * 2 + 'all descendants' | INCLUDE_ALL_DESCENDANTS || TimeUnit.SECONDS.toMillis(2) | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE } def 'Query ancestors with #scenario.'() { when: 'query data nodes (using a fresh anchor with identical data for each test)' stopWatch.start() - def result = objectUnderTest.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchor, '//openroadm-device[@ne-state="inservice"]/ancestor::openroadm-devices', fetchDescendantsOption) + def result = objectUnderTest.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm3', '//openroadm-device[@ne-state="inservice"]/ancestor::openroadm-devices', fetchDescendantsOption) stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() then: 'the expected number of nodes is returned' @@ -98,10 +102,10 @@ class QueryPerfTest extends CpsPerfTestBase { and: 'all data is read within #durationLimit ms' recordAndAssertPerformance("Query ancestors with ${scenario}", durationLimit, durationInMillis) where: 'the following parameters are used' - scenario | fetchDescendantsOption | anchor || durationLimit | expectedNumberOfDataNodes - 'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 15 | 1 - 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 60 | 1 + OPENROADM_DEVICES_PER_ANCHOR - 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 150 | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + scenario | fetchDescendantsOption || durationLimit | expectedNumberOfDataNodes + 'no descendants' | OMIT_DESCENDANTS || 100 | 1 + 'direct descendants' | DIRECT_CHILDREN_ONLY || 100 | 1 + OPENROADM_DEVICES_PER_ANCHOR + 'all descendants' | INCLUDE_ALL_DESCENDANTS || TimeUnit.SECONDS.toMillis(2) | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy index 6d856cc88..5bb8192e5 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy @@ -24,6 +24,8 @@ import java.time.OffsetDateTime import org.onap.cps.api.CpsDataService import org.onap.cps.integration.performance.base.CpsPerfTestBase +import java.util.concurrent.TimeUnit + class UpdatePerfTest extends CpsPerfTestBase { CpsDataService objectUnderTest @@ -40,52 +42,52 @@ class UpdatePerfTest extends CpsPerfTestBase { objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', parentNodeXpath, jsonData, now) stopWatch.stop() def updateDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'update duration is under 1000 milliseconds' + then: 'update completes within expected time' recordAndAssertPerformance('Update 1 data node', 600, updateDurationInMillis) } - def 'Batch update 10 data nodes with descendants'() { + def 'Batch update 100 data nodes with descendants'() { given: 'a list of data nodes to update as JSON' def innerNodeJson = readResourceDataFile('openroadm/innerNode.json') - def nodesJsonData = (20..30).collectEntries {[ + def nodesJsonData = (1..100).collectEntries {[ "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']", innerNodeJson.replace('NODE_ID_HERE', it.toString()) ]} when: 'the fragment entities are updated by the data nodes' stopWatch.start() - objectUnderTest.updateDataNodesAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', nodesJsonData, now) + objectUnderTest.updateDataNodesAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', nodesJsonData, now) stopWatch.stop() def updateDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'update duration is under 5000 milliseconds' - recordAndAssertPerformance('Update 10 data nodes', 4000, updateDurationInMillis) + then: 'update completes within expected time' + recordAndAssertPerformance('Update 100 data nodes', TimeUnit.SECONDS.toMillis(30), updateDurationInMillis) } - def 'Update leaves for 1 data node'() { + def 'Update leaves for 1 data node (twice)'() { given: 'Updated json for openroadm data' def jsonDataUpdated = "{'openroadm-device':{'device-id':'C201-7-1A-10','status':'fail','ne-state':'jeopardy'}}" def jsonDataOriginal = "{'openroadm-device':{'device-id':'C201-7-1A-10','status':'success','ne-state':'inservice'}}" when: 'update is performed for leaves' stopWatch.start() - objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm3', "/openroadm-devices", jsonDataUpdated, now) - objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm3', "/openroadm-devices", jsonDataOriginal, now) + objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', "/openroadm-devices", jsonDataUpdated, now) + objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', "/openroadm-devices", jsonDataOriginal, now) stopWatch.stop() def updateDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'update duration is under 650 milliseconds' - recordAndAssertPerformance('Update leaves for 1 data node', 650, updateDurationInMillis) + then: 'update completes within expected time' + recordAndAssertPerformance('Update leaves for 1 data node', 500, updateDurationInMillis) } - def 'Batch update leaves for 50 data nodes'() { + def 'Batch update leaves for 100 data nodes (twice)'() { given: 'Updated json for openroadm data' - def jsonDataUpdated = "{'openroadm-device':[" + (1..50).collect { "{'device-id':'C201-7-1A-" + it + "','status':'fail','ne-state':'jeopardy'}" }.join(",") + "]}" - def jsonDataOriginal = "{'openroadm-device':[" + (1..50).collect { "{'device-id':'C201-7-1A-" + it + "','status':'success','ne-state':'inservice'}" }.join(",") + "]}" + def jsonDataUpdated = "{'openroadm-device':[" + (1..100).collect { "{'device-id':'C201-7-1A-" + it + "','status':'fail','ne-state':'jeopardy'}" }.join(",") + "]}" + def jsonDataOriginal = "{'openroadm-device':[" + (1..100).collect { "{'device-id':'C201-7-1A-" + it + "','status':'success','ne-state':'inservice'}" }.join(",") + "]}" when: 'update is performed for leaves' stopWatch.start() - objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm4', "/openroadm-devices", jsonDataUpdated, now) - objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm4', "/openroadm-devices", jsonDataOriginal, now) + objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', "/openroadm-devices", jsonDataUpdated, now) + objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', "/openroadm-devices", jsonDataOriginal, now) stopWatch.stop() def updateDurationInMillis = stopWatch.getTotalTimeMillis() - then: 'update duration is under 700 milliseconds' - recordAndAssertPerformance('Batch update leaves for 50 data nodes', 700, updateDurationInMillis) + then: 'update completes within expected time' + recordAndAssertPerformance('Batch update leaves for 100 data nodes', TimeUnit.SECONDS.toMillis(1), updateDurationInMillis) } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy index 419ec6096..d03aec2d5 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy @@ -23,6 +23,8 @@ package org.onap.cps.integration.performance.cps import java.time.OffsetDateTime import org.onap.cps.integration.performance.base.CpsPerfTestBase +import java.util.concurrent.TimeUnit + class WritePerfTest extends CpsPerfTestBase { def 'Writing openroadm data has linear time.'() { @@ -36,19 +38,19 @@ class WritePerfTest extends CpsPerfTestBase { stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() then: 'the operation takes less than #expectedDuration' - recordAndAssertPerformance("Writing ${totalNodes} devices", expectedDuration, durationInMillis) + recordAndAssertPerformance("Writing ${totalNodes} devices", TimeUnit.SECONDS.toMillis(expectedDurationInSeconds), durationInMillis) cleanup: cpsDataService.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor', OffsetDateTime.now()) cpsAdminService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor') where: - totalNodes || expectedDuration - 50 || 2_500 - 100 || 4_000 - 200 || 8_000 - 400 || 16_000 -// 800 || 32_000 -// 1600 || 64_000 -// 3200 || 128_000 + totalNodes || expectedDurationInSeconds + 50 || 3 + 100 || 5 + 200 || 10 + 400 || 20 +// 800 || 40 +// 1600 || 80 +// 3200 || 160 } def 'Writing bookstore data has exponential time.'() { @@ -70,14 +72,14 @@ class WritePerfTest extends CpsPerfTestBase { cpsAdminService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor') where: totalBooks || expectedDuration - 400 || 200 - 800 || 500 - 1600 || 1_000 - 3200 || 2_500 - 6400 || 10_000 -// 12800 || 30_000 -// 25600 || 120_000 -// 51200 || 600_000 + 400 || 200 + 800 || 500 + 1600 || TimeUnit.SECONDS.toMillis(1) + 3200 || TimeUnit.SECONDS.toMillis(3) + 6400 || TimeUnit.SECONDS.toMillis(10) +// 12800 || TimeUnit.SECONDS.toMillis(30) +// 25600 || TimeUnit.SECONDS.toMillis(120) +// 51200 || TimeUnit.SECONDS.toMillis(600) } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy index bcb2d2fe5..d01216ee5 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy @@ -44,7 +44,7 @@ class CmHandleQueryPerfTest extends NcmpRegistryPerfTestBase { stopWatch.stop() def durationInMillis = stopWatch.getTotalTimeMillis() then: 'the required operations are performed within 1200 ms' - recordAndAssertPerformance("CpsPath Registry attributes Query", 500, durationInMillis) + recordAndAssertPerformance("CpsPath Registry attributes Query", 250, durationInMillis) and: 'all but 1 (other node) are returned' result.size() == 999 and: 'the tree contains all the expected descendants too' |