Below I am answering my own question. There is a possibility that the answer is not correct. So, I request you to point out any errors or inadequacies.
I am still unable to read the binary file and I could not locate the py2lcov so that I could try to try out the idea given by @Henry. However answer to my primary question, I have been able to figure out. Two passes of trace command were enough.
First pass to create the trace file using "--count --file <TraceFileName>" option. First time you run this command the file mentioned will not be there. So there will be warning. Second pass onwards the file will be used.
Second pass to print the report using the file generated in the previous step with "--report --file <TraceFileName>" option alongwith "--summary" to produce the summary I wanted.
While running the module(s) with trace command <SomeFile>.cover files will be produced. This will be the original code that gets executed. The beginning of each line has a number associated (which tells the number of invocations of that line). An empty or ">>>>>>" at the beginning shows that that line did not get called. You can write a simple parser program to produce a detailed coverage output and write it to an html or xml to produce 'coverage' module like output (or you can customise it to your needs). The ">>>>>>" is produced if "--missing" option is passed in both the steps otherwise a blank is produced for the lines not covered by the unittest.
In both the passes, if you don't include the "--ignore-dir" or "--ignore-module", all the core python modules that get called while executing your actual python file (TempDelete.py in the above example). Thus for example the python is located as "/usr/lib/python3.12" then passing "/usr/lib" will suppress producing the coverage of those files. For details see the official docs.
I am writing below the contents of the test_foo.py and the two commands/passes that I had to execute.
## Contents of test_foo.py
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
def not_covered(self):
print("This will never be printed.")
loader = unittest.TestLoader()
tests = loader.discover('.')
testRunner = unittest.runner.TextTestRunner()
testRunner.run(tests)
Pass 1
$ python3.12 -m trace --count --file ./coverage/coverage --ignore-dir=/usr/lib --missing -C ./coverage/ ./test_foo.py
The first pass printed the output as below, as expected
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
Skipping counts file './coverage/coverage': [Errno 2] No such file or directory: './coverage/coverage'
Pass 2
$ python3.12 -m trace --report --file ./coverage/coverage --ignore-dir=/usr/lib --missing --summary -C ./coverage/ ./test_foo.py
The second pass gave me the summary I was looking for.
lines cov% module (path)
18 94% test_foo (/home/somefolder/test_foo.py)
I am also pasting the test_foo**.cover** file to show how a .cover file looks like.
1: import unittest
2: class TestStringMethods(unittest.TestCase):
1: def test_upper(self):
2: self.assertEqual('foo'.upper(), 'FOO')
1: def test_isupper(self):
2: self.assertTrue('FOO'.isupper())
2: self.assertFalse('Foo'.isupper())
1: def test_split(self):
2: s = 'hello world'
2: self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
4: with self.assertRaises(TypeError):
2: s.split(2)
1: def not_covered(self):
>>>>>> print("This will never be printed.")
1: loader = unittest.TestLoader()
1: tests = loader.discover('.')
1: testRunner = unittest.runner.TextTestRunner()
1: testRunner.run(tests)
I think my main query is resolved but I still do not know how to read/decode the binary file. May be we are not meant to. We can however print the contents with binascii module using ascii.b2a_uu as below.
import binascii
with open("coverage/coverage", "rb") as file_handle:
while(read_content:=file_handle.read(45)):
binascii.b2a_uu(read_content)
It produces the output as below.
b'M*\'UQ "@H6 T N+W1E<W1?9F]O+G!Y<0%+ 71Q DL!*&@!2P-T<0-+ BAH\n'
b'M 4L%=\'$$2P$H: %+"\'1Q!4L!*&@!2PQT<09+ 2AH 4L3=\'$\'2P$H: %+%G1Q\n'
b'M"$L!*&@!2Q=T<0E+ 2A8+0 "]H;VUE+W!A<G1I8VQE+T1E<VMT;W O5&5M\n'
b'M<$1E;&5T92]T97-T7V9O;RYP>7$*2P%T<0M+ 2AH"DL#=\'$,2P(H: I+!71Q\n'
b'M#4L!*&@*2PAT<0Y+ 2AH"DL,=\'$/2P$H: I+$W1Q$$L!*&@*2Q9T<1%+ 2AH\n'
b'M"DL7=\'$22P$H: I+&\'1Q$TL!*&@*2QET<11+ 2AH"DL)=\'$52P(H: I+"G1Q\n'
b'M%DL"*&@*2PUT<1=+ BAH"DL.=\'$82P(H: I+$\'1Q&4L$*&@*2Q%T<1I+ BAH\n'
b'G"DL&=\'$;2P(H: %+&\'1Q\'$L!*&@!2QET<1U+ 75]<1Y]<1]T<2 N\n
However I still do not know how to decode it. If you happen to know the trick, please share.