Skip to content

Invalid code coverage report using sfdx force:source:deploy and force:mdapi:deploy #1568

@bzacharski

Description

@bzacharski

Summary

Tested on version 7.155.0 (salesforce/salesforcedx:7.155.0-full), 154 and 153
I wanted to use code coverage from deploy commands to feed sonarqube (json format) and gitlab (cobertura format) with code coverage information, but it is failing. I've analyzed reports from deploy commands and compared them to reports from force:apex:test:run command.

  1. For json format it is different between deploy and test:run commands
  2. It looks like reports from deploy commands are invalid. For example I have a class x on destination org and by running it using test:run command I received report that class has 13 total lines and 11 is covered. Which is correct, but both json and cobertura from deploy commands have entries for 15 lines of which 2 are not covered (0). I've checked for sure that I am deploying the same class. It is reporting lines that are empty, have only comments, etc. And it looks that data from deploy commands are invalid for all files. Class form example (I replaced x, y, z, v, w, s, t):
/**************************************************************************************
 *  @Class            : x
 *  @Author           : y
 *  @Version History  : 1.0
 *  @Date             : 
 *  @Description      : Handler class for  z.
****************************************************************************************/
public with sharing class x extends TriggerHandler {
    
    private static final string RE_OBJ_API = 'v';
    
    /* Class constructor which invokes parent constructor using standard super function */
    public x(){
        super(RE_OBJ_API );     
    } 
    /* To get the API name of the executing trigger context */
    public override String getName(){
        return RE_OBJ_API ;
    }
    public override  void beforeInsert(List<SObject> newItems){}
    
    /* To invoke before update trigger logic */
    public override void beforeUpdate(List<SObject> newItems, Map<Id, SObject> newItemsMap, Map<Id, SObject> oldItemsMap) {}
    
    /* To invoke before delete trigger logic */
    public override void beforeDelete(Map<Id, SObject> oldItemsMap) {
     w.s(oldItemsMap);
    }
    
    /* To invoke after update trigger logic */
    public override void afterUpdate(List<SObject> newItems, Map<Id, SObject> newItemsMap, Map<Id, SObject> oldItemsMap) {}
    
    /* To invoke after delete trigger logic */
    public override void afterDelete(Map<Id, SObject> oldItemsMap) {
        w.t(oldItemsMap);
    }
    
    /* To invoke after undelete trigger logic */
    public  override void afterUndelete(Map<Id, SObject> oldItemsMap) {}
    
    /* To invoke after insert trigger logic */
    public override void afterInsert(List<SObject> newItems, Map<Id, SObject> newItemsMap) {}

}

test:run entry for given class:

{
    "id": "01p3N000009V0efQAC",
    "name": "x",
    "totalLines": 13,
    "lines": {
      "13": 1,
      "14": 1,
      "17": 0,
      "18": 0,
      "20": 1,
      "23": 1,
      "26": 1,
      "27": 1,
      "31": 1,
      "34": 1,
      "35": 1,
      "39": 1,
      "42": 1
    },
    "totalCovered": 11,
    "coveredPercent": 85
  }

deploy json entry for class:

"src/x": {
        "fnMap": {},
        "branchMap": {},
        "path": "src/x",
        "f": {},
        "b": {},
        "s": {
            "17": 0,
            "18": 0,
            "19": 1,
            "20": 1,
            "21": 1,
            "22": 1,
            "23": 1,
            "24": 1,
            "25": 1,
            "26": 1,
            "27": 1,
            "28": 1,
            "29": 1,
            "30": 1,
            "31": 1
        },
        "statementMap": {
            "17": {
                "start": {
                    "line": 17,
                    "column": 0
                },
                "end": {
                    "line": 17,
                    "column": 0
                }
            },
            "18": {
                "start": {
                    "line": 18,
                    "column": 0
                },
                "end": {
                    "line": 18,
                    "column": 0
                }
            },
            "19": {
                "start": {
                    "line": 19,
                    "column": 0
                },
                "end": {
                    "line": 19,
                    "column": 0
                }
            },
            "20": {
                "start": {
                    "line": 20,
                    "column": 0
                },
                "end": {
                    "line": 20,
                    "column": 0
                }
            },
            "21": {
                "start": {
                    "line": 21,
                    "column": 0
                },
                "end": {
                    "line": 21,
                    "column": 0
                }
            },
            "22": {
                "start": {
                    "line": 22,
                    "column": 0
                },
                "end": {
                    "line": 22,
                    "column": 0
                }
            },
            "23": {
                "start": {
                    "line": 23,
                    "column": 0
                },
                "end": {
                    "line": 23,
                    "column": 0
                }
            },
            "24": {
                "start": {
                    "line": 24,
                    "column": 0
                },
                "end": {
                    "line": 24,
                    "column": 0
                }
            },
            "25": {
                "start": {
                    "line": 25,
                    "column": 0
                },
                "end": {
                    "line": 25,
                    "column": 0
                }
            },
            "26": {
                "start": {
                    "line": 26,
                    "column": 0
                },
                "end": {
                    "line": 26,
                    "column": 0
                }
            },
            "27": {
                "start": {
                    "line": 27,
                    "column": 0
                },
                "end": {
                    "line": 27,
                    "column": 0
                }
            },
            "28": {
                "start": {
                    "line": 28,
                    "column": 0
                },
                "end": {
                    "line": 28,
                    "column": 0
                }
            },
            "29": {
                "start": {
                    "line": 29,
                    "column": 0
                },
                "end": {
                    "line": 29,
                    "column": 0
                }
            },
            "30": {
                "start": {
                    "line": 30,
                    "column": 0
                },
                "end": {
                    "line": 30,
                    "column": 0
                }
            },
            "31": {
                "start": {
                    "line": 31,
                    "column": 0
                },
                "end": {
                    "line": 31,
                    "column": 0
                }
            }
        }
    }

deploy cobertura entry for class:

<class name="x" filename="src/x" line-rate="0.8665999999999999" branch-rate="1">
          <methods>
          </methods>
          <lines>
            <line number="17" hits="0" branch="false"/>
            <line number="18" hits="0" branch="false"/>
            <line number="19" hits="1" branch="false"/>
            <line number="20" hits="1" branch="false"/>
            <line number="21" hits="1" branch="false"/>
            <line number="22" hits="1" branch="false"/>
            <line number="23" hits="1" branch="false"/>
            <line number="24" hits="1" branch="false"/>
            <line number="25" hits="1" branch="false"/>
            <line number="26" hits="1" branch="false"/>
            <line number="27" hits="1" branch="false"/>
            <line number="28" hits="1" branch="false"/>
            <line number="29" hits="1" branch="false"/>
            <line number="30" hits="1" branch="false"/>
            <line number="31" hits="1" branch="false"/>
          </lines>
        </class>

Steps To Reproduce:

Create class similar to example, deploy to org, run test:run and gather json coverage report, deploy the same class using deploy command (mdapi or source) and gather json and/or cobertura coverage reports

Expected result

Deploy commands produce valid reports. Json reports for tesr:run and deploy commands have similar/identical structure.

Actual result

Reports from deploy commands are invalid. Json reports have different structure.

System Information

{
	"cliVersion": "sfdx-cli/7.155.0",
	"architecture": "linux-x[64](https://code.roche.com/dws/DWS/-/jobs/9233716#L64)",
	"nodeVersion": "node-v16.13.2",
	"pluginVersions": [
		"@oclif/plugin-autocomplete 0.3.0 (core)",
		"@oclif/plugin-commands 1.3.0 (core)",
		"@oclif/plugin-help 3.3.1 (core)",
		"@oclif/plugin-not-found 1.2.6 (core)",
		"@oclif/plugin-plugins 1.10.11 (core)",
		"@oclif/plugin-update 1.5.0 (core)",
		"@oclif/plugin-warn-if-update-available 1.7.3 (core)",
		"@oclif/plugin-which 1.0.4 (core)",
		"@salesforce/sfdx-plugin-lwc-test 0.1.7 (core)",
		"alias 2.0.1 (core)",
		"apex 0.13.0 (core)",
		"auth 2.1.0 (core)",
		"community 2.0.0 (core)",
		"config 1.4.12 (core)",
		"custom-metadata 2.0.0 (core)",
		"data 2.0.3 (core)",
		"generator 2.0.1 (core)",
		"info 2.0.1 (core)",
		"limits 2.0.1 (core)",
		"org 1.13.2 (core)",
		"salesforce-alm 54.5.0 (core)",
		"schema 2.1.1 (core)",
		"sfdx-cli 7.155.0 (core)",
		"signups 1.1.2 (core)",
		"source 1.10.2 (core)",
		"telemetry 2.0.0 (core)",
		"templates 54.8.0 (core)",
		"trust 2.0.1 (core)",
		"user 2.0.2 (core)"
	],
	"osVersion": "Linux 5.4.0-109-generic"
}

Additional information

Metadata

Metadata

Assignees

No one assigned

    Labels

    investigatingWe're actively investigating this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions