Update: Apple took this to heart, and fixed things in Xcode 5.1.
Bug or feature? “You have to call __gcov_flush() to collect coverage data with the iOS simulator.” According to Apple, this is a feature. But not if you actually want to measure your code coverage.
Code coverage… oy vey! Back in the days of running on iOS 6 using Xcode 4, measuring code coverage for unit tests was fairly straightforward. A set of coverage scripts I published made it easier still.
Then along came iOS 7 and Xcode 5. Coverage still worked if you continued to run on iOS 6. But on iOS 7, you’d get “ERROR: no .gcda files found” indicating that coverage data wasn’t being captured. “Well, maybe I need to switch from SenTestingKit to Apple’s new XCTest framework.” Nope, that didn’t help.
Call __gcov_flush() to Write Out Code Coverage Data
But I want to call it just once, after all tests have finished.
Folks starting posting questions on the Apple Developer Forums. Apple’s answer was, “You have to call __gcov_flush() to collect coverage data with the iOS simulator.” There was no need to do this before because coverage data is also written when the app exits, which apparently used to happen at the conclusion of unit tests but “we have fixed that.”
Okay, so when do you call __gcov_flush()? The simplest way is to swizzle tearDown(). The problem with this that __gcov_flush() will be invoked after every test—for one of my projects, that’s over a thousand times! But I want to call it just once, after all tests have finished.
Test Observers to the Rescue
Dave MacLachlan, author of CoverStory, offers a better way, made available through Google Toolbox for Mac (GTM). SenTestingKit and XCTest both have mechanisms for test observers, which Dave puts to work. Here’s how to put his code into your tests:
- Get a copy of the latest GTM source code. You can get it straight from the official repository. I’ve also made a mirror on GitHub.
- Add UnitTesting/GTMCodeCoverageApp.h and .m to your application. Don’t put them in your test target, they belong in your application target.
If you use XCTest, add GTM_USING_XCTEST=1 to the “Preprocessor Macros” build setting for your application target. - If you use SenTestingKit, add GTMCodeCoverageTestsST.m to your test target.
If you use XCTest, add GTMCodeCoverageTestsXC.m instead. - Add GTM_IS_COVERAGE_BUILD=1 to “Preprocessor Macros” for your coverage configuration. Do this at the project level so that it goes into both the app target and the test target.
And voilà, your code coverage data is back. Thanks, Dave!
Remember, this is legacy information. Apple fixed things in subsequent releases of Xcode, so we don’t need to do all this anymore.
Worked like a charm. Thanks for this post =)
Dear Jon
I have the following setup:
* StaticLibrary
* Application that depends upon StaticLibrary
and I would like to generate a coverage report only for StaticLibrary.
I set up the environment as suggested above (except that my StaticLibrary target plays the role of your application target). I can generate the coverage report for StaticLibrary but using the setup above Application fails to link and I get
Undefined symbols for architecture i386:
"___gcov_flush", referenced from:
-[UIApplication(GTMCodeCoverage) gtm_gcov_flush] in libStaticLibrary.a(GTMCodeCoverageApp.o)
"_llvm_gcda_emit_arcs", referenced from:
___llvm_gcov_writeout in libStaticLibrary.a(SampleComponent.o)
___llvm_gcov_writeout in libStaticLibrary.a(StaticLibrary.o)
___llvm_gcov_writeout in libStaticLibrary.a(GTMCodeCoverageApp.o)
"_llvm_gcda_emit_function", referenced from:
...
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
If I set
Generate Test Coverage File = YES
Instrument Program Flow = YES
for Application then it builds OK. But I would not like to mandate these settings for the application.
Can you help me what could be wrong?
Thanks in advance!
Eventually, I found the following article:
https://www.cocoanetics.com/2013/10/xcode-coverage/
It describes my issue in section “Configuration Woes”. Having defined a separate “Coverage” build configuration solved the problem.
Richard, I’m glad you figured out the separate build configuration trick. And that Cocoanetics article is really good — very thorough.
This linked solution is nice, however I figured if its Debug configuration then it’ll be OK to instrument the code . . . so I turn on coverage collection for Debug, and turn it off for Release.
Jasper, I’ve been doing the same. When I say “your coverage configuration,” I mean whatever configuration you use for coverage.
This works perfectly, thank you!
Thank you Jon Reid your post helped me :)
Hey is there a possibility to hide or ignore the GTMCodeCoverageApp.m file in my code coverage report?
Even though GTMCodeCoverageApp.m gets built into the main app, I put it with my other test support files in a TestSupport folder. Then in the exclude_data() function, I exclude "TestSupport/*" and that takes care of everything in that folder.
Hi ,
for get the code coverage i have follow the step
1)write Flush in viewController.m
- (void)viewDidLoad{
__gcov_flush() ;
[super viewDidLoad];
}
-(BOOL)Hello{
return TRUE;
}
2)Make test case file.
- (void)setUp{
controller = [[ViewController alloc]init];
[super setUp];
}
- (void)tearDown{
__gcov_flush() ;
[super tearDown];
}
-(void)testHello
{
if(![controller Hello])
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
3)Compile it .
But here i couldn’t get correct code coverage.
I have been written three function in Viewcontroller.m Hello(), Hello1(),Hello2()
without unit test case ,it gives the code coverage .
How it give the code coverage?
Can give me idea to get correct unit test coverage as per written function ?
Thanks,
Mahen
Mahen, see my latest update Code Coverage Fixed for Xcode 5.1 and see if that helps.
Having upgraded to XCode 5.1 lcov started to fail processing coverage data. It says:
gcov: Unknown command line argument '-v'. Try: '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/gcov -help'
gcov: Did you mean '-a'?
gcov: Not enough positional command line arguments specified!
Must specify at least 1 positional arguments: See: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/gcov -help
I assume this is because XCode 5 installed a new version of gcov and its switches and options seem to have changed.
It seems that the old gcov was saved as gcov-4.2 so I added the –gvov-tool switch to the lcov command line in getcov.sh but all I could get was an empty coverage report.
Have you bumped in the same issue?
Do you have a solution for this?
Thanks in advance.
I’m experiencing the same issue, having started my test coverage setup only yesterday. I posted the current state of my investigation to StackOverflow if you want to follow: http://stackoverflow.com/questions/22343725/xcode-5-1-unit-test-coverage-analysis-fails-on-files-using-blocks
Same issue here. Changing
-v
to--version
in lcov doesn’t prevent it from erroring out. If I point lcov to gcov-4.2, I get a coverage report, but it only shows a tiny fraction of the source code.The problem is in the LCOV 1.10 geninfo script. I found the solution. See my answer on Stack Overflow.
There remain issues measuring code containing blocks. While the above fix restores source/line coverage, function coverage is now absent.
At long last, I have a solution: see Code Coverage Fixed for Xcode 5.1
Hello Jon,
I have tried Code Coverage Fixed for Xcode 5.1, but i am not able to get the report.I have tried one thing here, i have commented all the enumerated blocks inside the file and tried executing the test. This gives the result. I think there is a problem with using enumerated blocks inside the file. I was following the below post also.
http://stackoverflow.com/questions/22343725/xcode-5-1-unit-test-coverage-analysis-fails-on-files-using-blocks
Please suggest me a way out from this.
Thanks
Shinoj
Hi,
I’ve followed through the tutorial (both this and ‘How to Measure Code Coverage’). Adding Google’s toolkit seems to result in this error:
Undefined symbols for architecture i386:
"___gcov_flush", referenced from:
-[GTMCodeCoverageTests stopObserving] in GTMCodeCoverageTestsXC.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
can anyone advise?
Dave, it looks like you haven’t enabled code coverage for your build configuration. Make sure to enable “Generate Test Coverage Files” and “Instrument Program Flow”.
We don’t need the GTM workaround anymore with Xcode 5.1 — but you’ll still need those flags for coverage.
Hi,
I am a test engineer and I am trying to do code coverage for an iOS application. I am using XCode 5.0.2 and gcovr 3.1 to test and perform code coverage for iOS 7 devices. I initially had issues with .gcda file generation issue. I followed your blog and resolved the same.
I have set ‘Generate test coverage files’ to ‘Yes’, ‘Instrument Program flow’ to ‘Yes’ for the Debug module only. I have also set ‘ -fprofile-arcs -ftest-coverage’ for Debug module in ‘Other C Flags’. I have added ‘__gcov_flush()’ in my app code when application terminates.
Now .gcda files are generating without issues. I copy that to my workspace in Home directory. From that location, I try to run code coverage for the application. I execute the gcovr command mentioning –object-directory and gcov command runs. I am getting “Gathered coveraged data for 0 files” at the end.
Kindly help me to resolve the same.
Jai, why are you moving the .gcda files?
I see from https://code.google.com/p/google-toolbox-for-mac/source/browse/trunk/UnitTesting/GTMCodeCoverageTestsXC.m that Xcode 6 has deprecated XCTestObserver and XCTestLog without having a replacement.
Thoughts?
As per Jon’s https://twitter.com/qcoding/status/515594893373472768, this entire workaround seems to be redundant with Xcode 6.0.1.
Start by taking out the GTM_USING_XCTEST=1 and GTM_IS_COVERAGE_BUILD=1 preprocessor macros (fortunately Xcode 6’s search now looks in your project file, not just the source files, so that’s easy).
Nuke the DerivedData folder and re-run tests. If, like me, you get good .gcda files from that, you can remove the Google Toolbox code from your project (unless of course you’re using Google Toolbox for other reasons).
Thanks for adding this, Richard! This was a point of confusion echoed by someone at NSSpain. I’ve added “[Legacy]” to the title.
How to exclude View Controller Default methods from Code Coverage.
like ViewDidLoad,ViewWillAppear,ViewDidReceiveMemoryWarning…….
Srikanth, rather than exclude such things, I’d want test coverage on those methods.
Hi,
Is it possible to get code coverage from iOS UI tests written using Appium tool?
Thanks,
Ganesan.
Ganesan,
If your Appium tests continually quit & relaunch (which is fairly typical with UI tests), that would present a challenge to gathering cumulative test coverage. If that’s the case, what you’d need to do is add a step after the app is killed each time to take the coverage data for that run and merge it with previous tests.
Thank you:) It Works!
@Ganesan
How to get code coverage from iOS app UI tests written using Appium tool? Can you provide some example?
Hi,
how can you did the code coverage with appium.. can you pls let me know.
Sorry, I’ve never used Appium.
Hi Jon,
I am using Xcode 7.3.1
I have few libraries as .xcodeproj
I am executing recorded UI tests.
I have set flags (“Generate Test Coverage Files” and “Instrument Program Flow”) in my library project as well as main project. I have enabled “Generate Coverage Data” in Coverage scheme of main project.
I am getting coverage for main project only. I want coverage of my libraries.
Please help..
Raphael,
First, note that XcodeCoverage will only measure Objective-C and C line coverage. It won’t measure Swift. That’s because it uses gcov, which Apple has replaced.
Edit the script getcov and find the gather_coverage() function. You should see a line like
LCOV --capture --derive-func-data -b "${SRCROOT}" -d "${OBJ_DIR}" -o "${LCOV_INFO}"
That only covers the main target. To add other targets, you’ll need to capture them explicitly into separate info files, then combine them. First, let’s edit the envcov.sh script. Define the location of your framework’s object files. Something like this:
MYLIB_OBJ=${OBJROOT}/MyLib.build/Coverage-iphonesimulator/MyLib.build/Objects-normal/${NATIVE_ARCH}
With that definition in place, add it to cleancov:
LCOV --zerocounters -d "${MYLIB_OBJ}"
Now over in getcov, we need to edit gather_coverage(). Capture the coverage data from your framework to a separate .info file:
LCOV --capture -b "${SRCROOT}/path-to-MyLib-source" -d "${MYLIB_OBJ}" -o MyLib.info
Once we’ve collected .info for each unit, it’s time to append them together to the main report:
LCOV -a ${LCOV_INFO} -a MyLib.info -o ${LCOV_INFO}
Hope this helps. I’m sure I’ve missed a detail here or there, but this should get you close. Good luck!