Skip to content

πŸ“Š Mutation testing β€” weekly baselineΒ #591

Description

@github-actions

Score: 67.2% (↑ +0.1 vs prior)

Per-shard breakdown

Shard Score Killed Survived No cov Timeout Errors Total
mutation-report-db β€” β€” β€” β€” β€” β€” (missing)
hardware-lib 67.5% 1619 591 180 7 969 3391
homekit 62.6% 290 164 9 0 201 689
hooks 73.5% 886 296 23 1 483 1707
server 59.5% 1421 595 371 2 1682 5117
services-scheduler 73.2% 561 186 16 3 251 1019
streaming 73.1% 890 252 63 13 411 1638
  • HTML reports: attached as mutation-report-<shard> artifacts on run 29145443368.
  • Hit list artifact: mutation-hitlist on the same run.
  • Last updated: 2026-07-11T09:31:49.682Z (commit cf895d7, trigger: schedule).

This issue is updated automatically by .github/workflows/mutation.yml on each scheduled / manual mutation run. Do not edit the body β€” it will be overwritten. Comment freely to track work on surviving mutants.


Mutation testing β€” hit list

Score: 67.2% Β· Total mutants: 13561

Status Count
Killed 5667
Survived 2084
NoCoverage 662
Timeout 26
CompileError 3996
RuntimeError 1
Ignored 1125

Surviving mutants by file (80 files, top 50 mutants shown)

For each entry below: the mutator type tells you what Stryker changed, the snippet shows where, and "expected test" is the most likely location to add a killing assertion. A killing test must fail when the mutation is applied.

src/streaming/piezoStream.ts β€” 151 surviving

Expected test file: src/streaming/tests/piezoStream.test.ts

LogicalOperator at line 47
    46 | 
>   47 | const WS_PORT = Number(process.env.PIEZO_WS_PORT ?? 3001)
    48 | const RAW_DATA_DIR = process.env.RAW_DATA_DIR ?? '/persistent'

+ (mutation): process.env.PIEZO_WS_PORT && 3001
StringLiteral at line 48
    47 | const WS_PORT = Number(process.env.PIEZO_WS_PORT ?? 3001)
>   48 | const RAW_DATA_DIR = process.env.RAW_DATA_DIR ?? '/persistent'
    49 | // Fallback subdirectory probed when RAW_DATA_DIR holds no usable .RAW files.

+ (mutation): ""
ArithmeticOperator at line 57
    56 | // index stays bounded on long-running streams (24h at 50fps was ~70MB).
>   57 | const FRAME_INDEX_RETENTION_S = SEEK_MAX_DURATION_S + 10
    58 | // Drop outbound frames for a client whose send buffer exceeds this many bytes.

+ (mutation): SEEK_MAX_DURATION_S - 10
ArithmeticOperator at line 60
    59 | // Prevents a slow/stalled client from pinning unbounded server memory.
>   60 | const MAX_BUFFERED_BYTES = 1024 * 1024 // 1 MB
    61 | // Cap inbound client messages. All client→server messages are tiny JSON

+ (mutation): 1024 / 1024
ObjectLiteral at line 317
   316 | 
>  317 | const cborDecoder = new Decoder({ mapsAsObjects: true, useRecords: false })
   318 | 

+ (mutation): {}
BooleanLiteral at line 317
   316 | 
>  317 | const cborDecoder = new Decoder({ mapsAsObjects: true, useRecords: false })
   318 | 

+ (mutation): false
BooleanLiteral at line 317
   316 | 
>  317 | const cborDecoder = new Decoder({ mapsAsObjects: true, useRecords: false })
   318 | 

+ (mutation): true
StringLiteral at line 52
    51 | // (and the SEQNO.RAW tmpfs symlink) sits at the /persistent root.
>   52 | const RAW_DATA_FALLBACK_SUBDIR = 'biometrics'
    53 | const FILE_POLL_INTERVAL_MS = 10 // match Python's 10 ms poll for new data

+ (mutation): ""
ConditionalExpression at line 127
   126 |   // slice is correct. Batch the splice to avoid O(n) shift per push.
>  127 |   if (frameIndex.length > 0 && frameIndex[0].ts < cutoff) {
   128 |     let drop = 0

+ (mutation): true
LogicalOperator at line 127
   126 |   // slice is correct. Batch the splice to avoid O(n) shift per push.
>  127 |   if (frameIndex.length > 0 && frameIndex[0].ts < cutoff) {
   128 |     let drop = 0

+ (mutation): frameIndex.length > 0 || frameIndex[0].ts < cutoff
ConditionalExpression at line 127
   126 |   // slice is correct. Batch the splice to avoid O(n) shift per push.
>  127 |   if (frameIndex.length > 0 && frameIndex[0].ts < cutoff) {
   128 |     let drop = 0

+ (mutation): true
EqualityOperator at line 127
   126 |   // slice is correct. Batch the splice to avoid O(n) shift per push.
>  127 |   if (frameIndex.length > 0 && frameIndex[0].ts < cutoff) {
   128 |     let drop = 0

+ (mutation): frameIndex.length >= 0
ConditionalExpression at line 127
   126 |   // slice is correct. Batch the splice to avoid O(n) shift per push.
>  127 |   if (frameIndex.length > 0 && frameIndex[0].ts < cutoff) {
   128 |     let drop = 0

+ (mutation): true
EqualityOperator at line 127
   126 |   // slice is correct. Batch the splice to avoid O(n) shift per push.
>  127 |   if (frameIndex.length > 0 && frameIndex[0].ts < cutoff) {
   128 |     let drop = 0

+ (mutation): frameIndex[0].ts <= cutoff
ConditionalExpression at line 129
   128 |     let drop = 0
>  129 |     while (drop < frameIndex.length && frameIndex[drop].ts < cutoff) drop += 1
   130 |     if (drop > 0) frameIndex.splice(0, drop)

+ (mutation): true
EqualityOperator at line 129
   128 |     let drop = 0
>  129 |     while (drop < frameIndex.length && frameIndex[drop].ts < cutoff) drop += 1
   130 |     if (drop > 0) frameIndex.splice(0, drop)

+ (mutation): drop <= frameIndex.length
EqualityOperator at line 129
   128 |     let drop = 0
>  129 |     while (drop < frameIndex.length && frameIndex[drop].ts < cutoff) drop += 1
   130 |     if (drop > 0) frameIndex.splice(0, drop)

+ (mutation): frameIndex[drop].ts <= cutoff
ConditionalExpression at line 130
   129 |     while (drop < frameIndex.length && frameIndex[drop].ts < cutoff) drop += 1
>  130 |     if (drop > 0) frameIndex.splice(0, drop)
   131 |   }

+ (mutation): true
EqualityOperator at line 130
   129 |     while (drop < frameIndex.length && frameIndex[drop].ts < cutoff) drop += 1
>  130 |     if (drop > 0) frameIndex.splice(0, drop)
   131 |   }

+ (mutation): drop >= 0
StringLiteral at line 154
   153 |   function need(n: number): void {
>  154 |     if (pos + n > end) throw new RangeError('Incomplete record')
   155 |   }

+ (mutation): ""
ConditionalExpression at line 159
   158 |   need(1)
>  159 |   if (buf[pos] !== 0xa2) {
   160 |     throw new Error(`Expected outer map 0xa2, got 0x${buf[pos].toString(16)}`)

+ (mutation): false
BlockStatement at line 159
   158 |   need(1)
>  159 |   if (buf[pos] !== 0xa2) {
   160 |     throw new Error(`Expected outer map 0xa2, got 0x${buf[pos].toString(16)}`)
   161 |   }
   162 |   pos += 1

+ (mutation): {}
StringLiteral at line 160
   159 |   if (buf[pos] !== 0xa2) {
>  160 |     throw new Error(`Expected outer map 0xa2, got 0x${buf[pos].toString(16)}`)
   161 |   }

+ (mutation): ``
ConditionalExpression at line 167
   166 |   if (
>  167 |     buf[pos] !== 0x63
   168 |     || buf[pos + 1] !== 0x73
   169 |     || buf[pos + 2] !== 0x65
   170 |     || buf[pos + 3] !== 0x71
   171 |   ) {

+ (mutation): false
LogicalOperator at line 167
   166 |   if (
>  167 |     buf[pos] !== 0x63
   168 |     || buf[pos + 1] !== 0x73
   169 |     || buf[pos + 2] !== 0x65
   170 |     || buf[pos + 3] !== 0x71
   171 |   ) {

+ (mutation): (buf[pos] !== 0x63 || buf[pos + 1] !== 0x73 || buf[pos + 2] !== 0x65) && buf[pos + 3] !== 0x71
ConditionalExpression at line 167
   166 |   if (
>  167 |     buf[pos] !== 0x63
   168 |     || buf[pos + 1] !== 0x73
   169 |     || buf[pos + 2] !== 0x65
   170 |     || buf[pos + 3] !== 0x71

+ (mutation): false
LogicalOperator at line 167
   166 |   if (
>  167 |     buf[pos] !== 0x63
   168 |     || buf[pos + 1] !== 0x73
   169 |     || buf[pos + 2] !== 0x65
   170 |     || buf[pos + 3] !== 0x71

+ (mutation): (buf[pos] !== 0x63 || buf[pos + 1] !== 0x73) && buf[pos + 2] !== 0x65
ConditionalExpression at line 167
   166 |   if (
>  167 |     buf[pos] !== 0x63
   168 |     || buf[pos + 1] !== 0x73
   169 |     || buf[pos + 2] !== 0x65

+ (mutation): false
LogicalOperator at line 167
   166 |   if (
>  167 |     buf[pos] !== 0x63
   168 |     || buf[pos + 1] !== 0x73
   169 |     || buf[pos + 2] !== 0x65

+ (mutation): buf[pos] !== 0x63 && buf[pos + 1] !== 0x73
ConditionalExpression at line 167
   166 |   if (
>  167 |     buf[pos] !== 0x63
   168 |     || buf[pos + 1] !== 0x73

+ (mutation): false
ConditionalExpression at line 168
   167 |     buf[pos] !== 0x63
>  168 |     || buf[pos + 1] !== 0x73
   169 |     || buf[pos + 2] !== 0x65

+ (mutation): false
ConditionalExpression at line 169
   168 |     || buf[pos + 1] !== 0x73
>  169 |     || buf[pos + 2] !== 0x65
   170 |     || buf[pos + 3] !== 0x71

+ (mutation): false
ConditionalExpression at line 170
   169 |     || buf[pos + 2] !== 0x65
>  170 |     || buf[pos + 3] !== 0x71
   171 |   ) {

+ (mutation): false
ConditionalExpression at line 182
   181 |   const seqMt = seqHdr >> 5
>  182 |   if (seqMt !== 0) {
   183 |     throw new Error(`seq must be unsigned int, got major type ${seqMt}`)

+ (mutation): false
ConditionalExpression at line 188
   187 |   }
>  188 |   else if (seqAi === 24) {
   189 |     need(1)

+ (mutation): true
ConditionalExpression at line 211
   210 |   if (
>  211 |     buf[pos] !== 0x64
   212 |     || buf[pos + 1] !== 0x64
   213 |     || buf[pos + 2] !== 0x61
   214 |     || buf[pos + 3] !== 0x74
   215 |     || buf[pos + 4] !== 0x61
   216 |   ) {

+ (mutation): false
LogicalOperator at line 211
   210 |   if (
>  211 |     buf[pos] !== 0x64
   212 |     || buf[pos + 1] !== 0x64
   213 |     || buf[pos + 2] !== 0x61
   214 |     || buf[pos + 3] !== 0x74
   215 |     || buf[pos + 4] !== 0x61
   216 |   ) {

+ (mutation): (buf[pos] !== 0x64 || buf[pos + 1] !== 0x64 || buf[pos + 2] !== 0x61 || buf[pos + 3] !== 0x74) && buf[pos + 4] !== 0x61
ConditionalExpression at line 211
   210 |   if (
>  211 |     buf[pos] !== 0x64
   212 |     || buf[pos + 1] !== 0x64
   213 |     || buf[pos + 2] !== 0x61
   214 |     || buf[pos + 3] !== 0x74
   215 |     || buf[pos + 4] !== 0x61

+ (mutation): false
LogicalOperator at line 211
   210 |   if (
>  211 |     buf[pos] !== 0x64
   212 |     || buf[pos + 1] !== 0x64
   213 |     || buf[pos + 2] !== 0x61
   214 |     || buf[pos + 3] !== 0x74
   215 |     || buf[pos + 4] !== 0x61

+ (mutation): (buf[pos] !== 0x64 || buf[pos + 1] !== 0x64 || buf[pos + 2] !== 0x61) && buf[pos + 3] !== 0x74
ConditionalExpression at line 211
   210 |   if (
>  211 |     buf[pos] !== 0x64
   212 |     || buf[pos + 1] !== 0x64
   213 |     || buf[pos + 2] !== 0x61
   214 |     || buf[pos + 3] !== 0x74

+ (mutation): false
LogicalOperator at line 211
   210 |   if (
>  211 |     buf[pos] !== 0x64
   212 |     || buf[pos + 1] !== 0x64
   213 |     || buf[pos + 2] !== 0x61
   214 |     || buf[pos + 3] !== 0x74

+ (mutation): (buf[pos] !== 0x64 || buf[pos + 1] !== 0x64) && buf[pos + 2] !== 0x61
ConditionalExpression at line 211
   210 |   if (
>  211 |     buf[pos] !== 0x64
   212 |     || buf[pos + 1] !== 0x64
   213 |     || buf[pos + 2] !== 0x61

+ (mutation): false
LogicalOperator at line 211
   210 |   if (
>  211 |     buf[pos] !== 0x64
   212 |     || buf[pos + 1] !== 0x64
   213 |     || buf[pos + 2] !== 0x61

+ (mutation): buf[pos] !== 0x64 && buf[pos + 1] !== 0x64
ConditionalExpression at line 211
   210 |   if (
>  211 |     buf[pos] !== 0x64
   212 |     || buf[pos + 1] !== 0x64

+ (mutation): false
ConditionalExpression at line 212
   211 |     buf[pos] !== 0x64
>  212 |     || buf[pos + 1] !== 0x64
   213 |     || buf[pos + 2] !== 0x61

+ (mutation): false
ConditionalExpression at line 213
   212 |     || buf[pos + 1] !== 0x64
>  213 |     || buf[pos + 2] !== 0x61
   214 |     || buf[pos + 3] !== 0x74

+ (mutation): false
ConditionalExpression at line 214
   213 |     || buf[pos + 2] !== 0x61
>  214 |     || buf[pos + 3] !== 0x74
   215 |     || buf[pos + 4] !== 0x61

+ (mutation): false
ConditionalExpression at line 215
   214 |     || buf[pos + 3] !== 0x74
>  215 |     || buf[pos + 4] !== 0x61
   216 |   ) {

+ (mutation): false
ConditionalExpression at line 227
   226 |   let length: number
>  227 |   if (ai <= 23) {
   228 |     length = ai

+ (mutation): false
EqualityOperator at line 227
   226 |   let length: number
>  227 |   if (ai <= 23) {
   228 |     length = ai

+ (mutation): ai < 23

…and 2034 more surviving mutants not shown. See the HTML report for the full list.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions