ETSI CIM · Testing Task Force

How much of NGSI-LD does the test suite test?

TS 104-175 (the API) and TS 104-176 (the HTTP binding) are now published, so there is a fixed target to measure the suite against. The full suite was run against a broker, and two things were measured: which parts of the API the suite's requests use, and how much of the broker's code those requests reach.

50 / 90
operations exercised (method × resource, HEAD counted on its own)
84 / 275
URL parameters the spec lists per operation, given at least once
135 / 286
status codes the spec lists per operation, seen at least once
58 %
of the broker's NGSI-LD code that the run reaches (its own tests reach 84 %)

1. API coverage

Every request a Test Case makes goes through RequestsLibrary, which writes the method, the URL, the headers and the response status into output.xml. A script maps each of these requests to its operation in TS 104-176 and counts, for each operation:

The operations and the parameters depend only on the Test Cases. The status codes also depend on the broker.

By clause of TS 104-176

ClauseResourcesOperationsURL parametersStatus codes
§7Context information27/4036/14276/139
§8Temporal evolution9/1132/8424/35
§9Context sources10/1412/3925/40
§10@context management4/64/910/18
§11EntityMaps0/10-0/27
§12Context source identity0/2-0/4
§13Snapshots0/70/10/23
All50/9084/275135/286

Not one Test Case exists for these 40 operations:

Among the operations that are tested, the parameters are the largest gap.

Status codes:

All 90 operations

Highlighted rows are operations with no request. In the last column, the parameters never given come first, and the status codes never received follow in bold.

§MethodResource / operationReqTCsParamsCodesNever exercised
7.2.3.1GET/entities/
Query Entities
23123118/282/5collation containedBy csf entityMap entityMapLifetime geometryProperty lang orderFrom orderGeometry splitEntities
201 203 501
7.2.3.1HEAD/entities/
Query Entities
000/280/3attrs collation containedBy coordinates csf datasetId entityMap entityMapLifetime expandValues geometry geometryProperty geoproperty georel id idPattern join joinLevel jsonKeys lang omit orderBy orderFrom orderGeometry pick q scopeQ splitEntities type
204 400 501
7.2.3.2POST/entities/
Create Entity
6784660/04/5422
7.2.3.3DELETE/entities/
Purge Entities
13135/142/3coordinates csf geometry geometryProperty geoproperty georel idPattern scopeQ splitEntities
207
7.3.3.1GET/entities/{entityId}
Retrieve Entity
2472337/123/6containedBy datasetId entityMap entityMapLifetime type
201 203 501
7.3.3.1HEAD/entities/{entityId}
Retrieve Entity
000/120/4attrs containedBy datasetId entityMap entityMapLifetime geometryProperty join joinLevel lang omit pick type
204 400 404 501
7.3.3.2PATCH/entities/{entityId}
Merge Entity
33330/53/4format lang observedAt options type
207
7.3.3.3PUT/entities/{entityId}
Replace Entity
990/13/4type
207
7.3.3.4DELETE/entities/{entityId}
Delete Entity
6094360/14/4type
7.4.3.1POST/entities/{entityId}/attrs/
Append Attributes
21211/24/4type
7.4.3.2PATCH/entities/{entityId}/attrs/
Update Attributes
83820/14/4type
7.5.3.1GET/entities/{entityId}/attrs/{attrId}
Retrieve Attribute Value
000/70/5datasetId entityMap entityMapLifetime join joinLevel lang type
200 203 400 404 501
7.5.3.1HEAD/entities/{entityId}/attrs/{attrId}
Retrieve Attribute Value
000/70/4datasetId entityMap entityMapLifetime join joinLevel lang type
204 400 404 501
7.5.3.2PATCH/entities/{entityId}/attrs/{attrId}
Partial Attribute Update
37370/13/4type
207
7.5.3.3PUT/entities/{entityId}/attrs/{attrId}
Replace Attribute
12120/13/4type
207
7.5.3.4DELETE/entities/{entityId}/attrs/{attrId}
Delete Attribute
39392/33/4type
207
7.6.3.1GET/entities/{entityId}/attrs/{attrId}/value
Retrieve Attribute Value
000/50/5datasetId entityMap entityMapLifetime lang type
200 203 400 404 501
7.6.3.1HEAD/entities/{entityId}/attrs/{attrId}/value
Retrieve Attribute Value
000/50/4datasetId entityMap entityMapLifetime lang type
204 400 404 501
7.6.3.2PUT/entities/{entityId}/attrs/{attrId}/value
Set Attribute Value
000/30/4datasetId observedAt type
204 207 400 404
7.7.3.1POST/entityOperations/create
Batch Entity Creation
22220/03/3
7.8.3.1POST/entityOperations/upsert
Batch Entity Creation or Update (Upsert)
25250/04/4
7.9.3.1POST/entityOperations/update
Batch Entity Update
16160/03/3
7.10.3.1POST/entityOperations/merge
Batch Entity Merge
10100/03/3
7.11.3.1POST/entityOperations/delete
Batch Entity Delete
69660/03/3
7.12.3.1POST/entityOperations/query
Query Entities
30300/01/4201 203 400
7.13.3.1GET/subscriptions/
Query Subscriptions
551/12/2
7.13.3.1HEAD/subscriptions/
Query Subscriptions
000/10/2limit
204 400
7.13.3.2POST/subscriptions/
Create Subscription
1401290/03/3
7.14.3.1GET/subscriptions/{subscriptionId}
Retrieve Subscription
30300/03/3
7.14.3.1HEAD/subscriptions/{subscriptionId}
Retrieve Subscription
000/00/3204 400 404
7.14.3.2PATCH/subscriptions/{subscriptionId}
Update Subscription
15140/03/3
7.14.3.3DELETE/subscriptions/{subscriptionId}
Delete Subscription
1311230/03/3
7.15.3.1GET/types/
Retrieve Available Entity Types / Retrieve Details of Available Entity Types
551/12/2
7.15.3.1HEAD/types/
Retrieve Available Entity Types / Retrieve Details of Available Entity Types
000/10/2details
204 400
7.16.3.1GET/types/{type}
Retrieve Available Entity Type Information
330/02/3400
7.16.3.1HEAD/types/{type}
Retrieve Available Entity Type Information
000/00/3204 400 404
7.17.3.1GET/attributes/
Retrieve Available Attributes / Retrieve Details of Available Attributes
441/11/2400
7.17.3.1HEAD/attributes/
Retrieve Available Attributes / Retrieve Details of Available Attributes
000/10/2details
204 400
7.18.3.1GET/attributes/{attrId}
Retrieve Available Attribute Information
330/02/3400
7.18.3.1HEAD/attributes/{attrId}
Retrieve Available Attribute Information
000/00/3204 400 404
8.2.3.1GET/temporal/entities/
Query Temporal Evolution of Entities
696920/292/4collation csf entityMap entityMapLifetime expandValues jsonKeys lang scopeQ splitEntities
201 203
8.2.3.1HEAD/temporal/entities/
Query Temporal Evolution of Entities
000/290/2aggrMethods aggrPeriodDuration attrs collation coordinates csf datasetId endTimeAt entityMap entityMapLifetime expandValues geometry geoproperty georel id idPattern jsonKeys lang lastN omit orderBy pick q scopeQ splitEntities timeAt timeproperty timerel type
204 400
8.2.3.2POST/temporal/entities/
Create or Update Temporal Evolution of an Entity
1761130/03/4422
8.3.3.1GET/temporal/entities/{entityId}
Retrieve Temporal Evolution of an Entity
868410/123/4datasetId lang
203
8.3.3.1HEAD/temporal/entities/{entityId}
Retrieve Temporal Evolution of an Entity
000/120/3aggrMethods aggrPeriodDuration attrs datasetId endTimeAt lang lastN omit pick timeAt timeproperty timerel
204 400 404
8.3.3.2DELETE/temporal/entities/{entityId}
Delete Temporal Evolution of an Entity
7825460/03/3
8.4.3.1POST/temporal/entities/{entityId}/attrs/
Add Attributes to Temporal Evolution of an Entity
550/03/3
8.5.3.1DELETE/temporal/entities/{entityId}/attrs/{attrId}
Delete Attribute from Temporal Evolution of an Entity
772/23/3
8.6.3.1PATCH/temporal/entities/{entityId}/attrs/{attrId}/{instanceId}
Modify attribute instance from Temporal Evolution of an Entity
770/03/3
8.6.3.2DELETE/temporal/entities/{entityId}/attrs/{attrId}/{instanceId}
Delete Attribute instance from Temporal Evolution of an Entity
770/03/3
8.7.3.1POST/temporal/entityOperations/query
Query Temporal Evolution of Entities
550/01/3201 400
9.2.3.1GET/csourceRegistrations/
Query Context Source Registrations
272712/192/2csf endTimeAt geometryProperty lang omit pick scopeQ
9.2.3.1HEAD/csourceRegistrations/
Query Context Source Registrations
000/190/2attrs coordinates csf endTimeAt geometry geometryProperty geoproperty georel id idPattern lang omit pick q scopeQ timeAt timeproperty timerel type
204 400
9.2.3.2POST/csourceRegistrations/
Register Context Source
2111710/03/4422
9.3.3.1GET/csourceRegistrations/{registrationId}
Retrieve Context Source Registration
20200/03/3
9.3.3.1HEAD/csourceRegistrations/{registrationId}
Retrieve Context Source Registration
000/00/3204 400 404
9.3.3.2PATCH/csourceRegistrations/{registrationId}
Update Context Source Registration
10100/03/3
9.3.3.3DELETE/csourceRegistrations/{registrationId}
Delete Context Source Registration
2061690/03/3
9.4.3.1GET/csourceSubscriptions/
Create Context Source Registration Subscription
11110/01/3201 409
9.4.3.1HEAD/csourceSubscriptions/
Create Context Source Registration Subscription
000/00/3204 400 409
9.4.3.2POST/csourceSubscriptions/
Query Context Source Registration Subscriptions
59440/11/2limit
200
9.5.3.1GET/csourceSubscriptions/{subscriptionId}
Retrieve Context Source Registration Subscription
13130/03/3
9.5.3.1HEAD/csourceSubscriptions/{subscriptionId}
Retrieve Context Source Registration Subscription
000/00/3204 400 404
9.5.3.2PATCH/csourceSubscriptions/{subscriptionId}
Update Context Source Registration Subscription
11110/03/3
9.5.3.3DELETE/csourceSubscriptions/{subscriptionId}
Delete Context Source Registration Subscription
55420/03/3
10.2.3.1GET/jsonldContexts/
List @contexts
43422/22/2
10.2.3.1HEAD/jsonldContexts/
List @contexts
000/20/2details kind
204 400
10.2.3.2POST/jsonldContexts/
Add @context
39270/02/2
10.3.3.1GET/jsonldContexts/{contextId}
Serve @context
30231/22/4core
400 422
10.3.3.1HEAD/jsonldContexts/{contextId}
Serve @context
000/20/4core details
204 400 404 422
10.3.3.2DELETE/jsonldContexts/{contextId}
Delete and Reload @context
112461/14/4
11.2.3.1GET/entityMaps/
Create EntityMap for Query Entities
000/00/4201 203 400 501
11.2.3.1HEAD/entityMaps/
Create EntityMap for Query Entities
000/00/3204 400 501
11.2.3.2POST/entityMaps/
Create EntityMap for Query Entities
000/00/2201 400
11.3.3.1GET/temporal/entityMaps
Create EntityMap for Query Temporal Evolution of Entities
000/00/2201 400
11.3.3.1HEAD/temporal/entityMaps
Create EntityMap for Query Temporal Evolution of Entities
000/00/2204 400
11.3.3.2POST/temporal/entityMaps
Create EntityMap for Query Temporal Evolution of Entities
000/00/2201 400
11.4.3.1GET/entityMaps/{entityMapId}
Retrieve EntityMap
000/00/3200 400 404
11.4.3.1HEAD/entityMaps/{entityMapId}
Retrieve EntityMap
000/00/3204 400 404
11.4.3.2PATCH/entityMaps/{entityMapId}
Update EntityMap
000/00/3204 400 404
11.4.3.3DELETE/entityMaps/{entityMapId}
Delete EntityMap
000/00/3204 400 404
12.2.3.1GET/info/sourceIdentity
Retrieve Context Source Identity Information
000/00/2200 501
12.2.3.1HEAD/info/sourceIdentity
Retrieve Context Source Identity Information
000/00/2204 501
13.2.3.1POST/snapshots/
Create Snapshot
000/00/3202 400 409
13.2.3.2DELETE/snapshots/
Purge Snapshots
000/10/4q
204 207 400 404
13.3.3.1GET/snapshots/{snapshotId}
Retrieve Snapshot Status
000/00/3200 400 404
13.3.3.1HEAD/snapshots/{snapshotId}
Retrieve Snapshot Status
000/00/3204 400 404
13.3.3.2PATCH/snapshots/{snapshotId}
Update Snapshot Status
000/00/3200 400 404
13.3.3.3DELETE/snapshots/{snapshotId}
Delete Snapshot
000/00/3204 400 404
13.4.3.1POST/snapshots/{snapshotId}/clone
Clone Snapshot
000/00/4202 400 404 409

2. Code coverage

The broker was built with coverage instrumentation. For comparison, the same build also ran its own functional tests (952 cases). That build had a few files fewer, so the percentages of the last column have slightly smaller denominators.

CodeLinesTest suiteBroker's own tests
NGSI-LD: the API, the HTTP binding, the database layer38 63958.0 %83.9 %
Broker-specific features, which no NGSI-LD test can reach11 9867.9 %70.1 %
All50 62546.1 %81.4 %
90 % is not a realistic target

The broker's own tests know its code, and they stop at 84 % of the NGSI-LD part. Most of the remaining 16 % is error handling that a test from outside cannot trigger: a database that fails, memory running out, internal errors. A suite that tests only through the API can realistically reach about 80 %, up from 58 %. API coverage is the better measure of the suite: it does not depend on any broker, and its target really is 100 %.

The functional areas where the broker's own tests reach far more code than the suite does:

3. Plan: 100 % of the API

1. Measure it in the suite done

The measurement is now a script in the suite, on the branch feat/api-coverage. It reads only the spec and a run's output.xml, so any broker gives the same operation and parameter counts. It takes about 3 seconds on a full run.

python3 scripts/api_coverage/api_coverage.py results/output.xml                         # the table
python3 scripts/api_coverage/api_coverage.py results/output.xml --json develop.json     # keep it
python3 scripts/api_coverage/api_coverage.py branch/output.xml --baseline develop.json  # what a branch adds

The inventory comes from the Markdown of TS 104-176 and records the spec commit it was built from. When a new version of the spec comes out, build_inventory.py builds it again.

2. HEAD done

TS 104-176 §5: a HEAD request functions as the GET request would, omits the body, and answers 204 No Content on success. The branch feat/head-test-cases adds 37 Test Cases in CommonBehaviours/CommonResponses/VerifyHEAD:

Measured with --baseline: operations 50 → 69, URL parameters 84 → 89, status codes 135 → 172. HEAD on /entityMaps/, /temporal/entityMaps, /entityMaps/{entityMapId} and /snapshots/{snapshotId} comes with step 3, together with the first tests of those resources.

3. The resources with no tests in progress

Retrieve Attribute, the Attribute value (§7.5, §7.6), the context source identity (§12), EntityMaps (§11) and Snapshots (§13): the happy path of each, plus the 400, 404 and 409 cases. That is about 80–100 Test Cases, and operations reach 90 of 90.

4. Parameters and status codes

Go through the 191 parameters and the 151 status codes that are never exercised, one operation at a time, starting with Query Entities and the temporal queries. That is about 250 Test Cases. This step moves the code coverage most, from 58 % towards 80 %.

5. Beyond the HTTP resources

A second inventory from TS 104-175 and TS 104-243 for what the resource tables do not show:

The method is the same, but these are read from the request and response bodies, not from the URL.

All in all: about 350–400 new Test Cases on top of today's 1 112, with each batch measured as it comes in.

4. The MRs

The schedule and the list of MRs are in the proposal to the TTF.

How this was measured