Unit tests failing without any message if linked to a custom framework - xcode

Update (From Build log):
"The bundle “Lib2Tests” couldn’t be loaded because it is damaged or missing necessary resources."
(dlopen_preflight(/Users/abc/Library/Developer/Xcode/DerivedData/Lib2-gaplfrtqethacsgkmjmlivcyuity/Build/Products/Debug-iphonesimulator/Lib2Tests.xctest/Lib2Tests):
Library not loaded: #rpath/Lib1.framework/Lib1
It seems the lib2.framework project only linked the Lib1.framework and it didn't embed it, and there's no option to embed the framework inside another cocoa touch framework, that's why at the end unit test failed as it couldn't find the referenced Lib1.framework.
put simply if FrameworkB is dependent on FrameworkA for some functionality down the road, UnitTests of FrameworkB will cease to run - if FrameworkA is linked to FrameworkB
Setup
I've created a simple isolated case to demonstrate and reproduce the problem.
I've 2 Swift Cocoa Touch Frameworks (Lib1 & Lib2 - Lib2 is dependent on Lib1 - single swift file with single unit test in each)
Lib2 unit tests will only work if you remove the linked Lib1. Interestingly both projects works on their own if run independently.
Question: is this a bug in Xcode? any workarounds?
Smallest Reproducible Example Project: https://www.dropbox.com/s/kycnvt1qvz8zw4o/LibTest.zip?dl=0
I'm attaching a screenshot, it's weird, no error messages or failure details in the console, all I get is a notification on the side the unit test failed. The test is not complicated, single line instantiating a class and it's runs successfully if I remove the link to my custom framework. (the custom framework itself also simple and runs/tests successfully on it's own) To observer this behavior simply delete the linked framework from the project files and run "Test" again. I believe the error is related to project files settings etc. and the solution requires to look into project file itself.

Related

Link problems in Xcode unit test of iOS app with embedded C++ code

I'm trying to fix our unit tests. The iOS app is complex, with a number of open-source libraries statically linked into the application binary, including Google protobuf and Boost. These are built as static-link libraries in separate targets in our iOS app Project, and linked into the main app.
Up until now, those libraries were also linked directly into the test target, even though the app hosts the testing. But I ran into an issue early in Google Protobuf initialization that makes me think the presence of that library both in the test target and in the main application target is a problem. So I removed the static libraries from the test target, thinking that the Objective-C++ methods that reference stuff in that library would still find it in the main app executable. This does not seem to be the case, as the linker is failing to link test files that reference C++ code defined in the static library linked into the application binary.
Objective-C and Swift code does seem to link correctly.
I'm not sure what to do to resolve this situation. As I understand Xcode unit testing, one normally does not include the code of the main application in the test target. But I'm not sure how to tell Xcode that my test files need to be able to link against arbitrary code in the app.
This is all in Xcode 10.1.
It's not uncommon to have to link against the same libraries in both production code and test code. Imagine a library with a Foo object, used in production. Some tests will want to make a Foo. But they can't just grab it out of the app. The tests need to link against the library, too.

failed to emit precompiled header

Good Afternoon
I am trying to implement googles heatmaps into my map based project.
In this particular project, I get the following errors when trying to build.
failed to emit precompiled header '/Users/zachwilcox/Library/Developer/Xcode/DerivedData/OddJobs-gbnkfettubuccoheinzfovrqefub/Build/Intermediates.noindex/PrecompiledHeaders/OddJobs-Bridging-Header-swift_3WGH9SNI96Z2-clang_16JN3VASQHE4C.pch' for bridging header '/Users/zachwilcox/Desktop/OddJobs/OddJobs-Bridging-Header.h'
and a follow up that says
'Google-Maps-iOS-Utils/GMUHeatmapTileLayer.h' file not found
I'm not sure why this is happening because I created an entirely new project and did the same implementation and it worked perfectly. But when I run it on my existing project I get these two errors.
I have been researching this problem for a while now and I can't figure out what the problem is. I have the Bridging header set to the .h file.
I have tried most steps that individuals have tried to give with their answers but for some reason, it's not working for my project. Thank you in advance. When reading googles installation guide, it said that all I needed to add was one bridging header file which is the code you see in the image below. When researching it, I have seen that I needed to add a .m file as well however I still get the same error when I do. again, in my dummy project, I don't have the .m file just everything you see below and everything compiles.
There are chances you have 'Find Implicit Dependencies" turned off for the selected Scheme. If you have updated/installed pods and try to build the project, it will more likely fail if this option is unchecked for the selected Scheme.
From Apple's Document:
Parallelize Build – This option allows Xcode to speed up total build time by building targets that do not depend on each other at the same time. This is a time-saver on projects with many smaller dependencies that can easily be run in parallel.
Find Implicit Dependencies – This is a very powerful option that allows Xcode to resolve what targets need to be built for the primary target of the scheme to be built successfully. However, this does come with some sharp edges that you have to be aware of.
Situation: You link a library against your application target and create an implicit dependency to that library’s target.
Scenario 1: “Find Implicit Dependencies” is enabled.
Result: The library will get built prior to building the application target. The application target will then link against the library and build successfully.
Scenario 2: “Find Implicit Dependencies” is disabled.
Result: The library will not get built prior to building the application target. The application target fail to build.
Fix: To ensure that the second scenario does not happen, you must add the necessary targets into the targets list and order them correctly.
If your project includes pod then you've to set the pod configuration for the test project too..
Select Root Project in File inspector -> Project Name -> Info -> Configurations
Set configuration for your test project same like your main project

Xcode 6 Testing Target Troubles

I've been going through many questions and answers here, and have ended up in a quandary.
Now that I'm happy with the state of my project code, I wanted to implement unit testing. So I added a testing target, can I get it to compile correctly? No.... I'm sure it's something simple, but I cannot work it out.
The project contains 2 3rd party private frameworks.
My test target bundle loader parameter is set to:
$(TEST_HOST)
My test target test host parameter is set to:
$(BUILT_PRODUCTS_DIR)/My App.app/Contents/MacOS/My App
When I try to compile, I get errors like this:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_MyArrayController", referenced from:
objc-class-ref in SMyAppLogicTests.o
I look this up, and find answers like this on:
XCTestCase: ld: symbol(s) not found for architecture x86_64
Basically saying I need to add my classes to my test target. This seems to kind of defeat the point building on top of the working app, but what the hey. So I add all .m files to the test target, and it compiles! Hooray, but, I have new problems:
Class MyClass is implemented in both /Users/georgebrown/Library/Developer/Xcode/DerivedData/My_App-akedavtbgqpujscilxqmhsvikcku/Build/Products/Debug/My App.app/Contents/MacOS/My App and /Users/georgebrown/Library/Developer/Xcode/DerivedData/My_App-akedavtbgqpujscilxqmhsvikcku/Build/Products/Debug/My App Tests.xctest/Contents/MacOS/My App Tests. One of the two will be used. Which one is undefined.
That makes sense, why do I have to put the m files in if they're already contained in my app. So I look it up, and get an answer that puts me back in the first position:
Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined
What am I missing? I've been stuck for a week just trying to implement testing in Xcode (Not to mention what Xcode server did to my server, I still have to figure that out), when I planned a 3-4 days for writing the tests foolishly thinking it would be a quick setup.
EDIT: I deleted my test target and started again. First thing that didn't work is that it couldn't find my 3rd part private frameworks. So I added $(PROJECT_DIR) to the frameworks search paths.
Next, I started getting the Undefined Symbols for architecture error again for my classes. I noticed my Bundle Loader was unset, so I set that to be $(TEST_HOST), now the tests run! Hooray.
Now I can't build for the Run Action, but build for Test works..... Progress I guess. I get the Undefined symbols for architecture x86_64: error again.
I've managed to fix that by editing the scheme, and deselecting "Run" for the test target on the build. I'm not sure I entirely understand how the scheme is supposed to work, however, it did the trick.
The XCTesting thingie works by injecting the test bundle into the app at runtime. When you add a new test target, Xcode takes care of setting up all that stuff for you: I'd suggest if you have a test target that is giving you grief, you create a new one in Xcode and transfer your test cases to that target.
Then you only have to follow three rules:
Add your class implementation files only to app targets, never to
test any test targets. This will prevent the "Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined" warning
Add your XCTestCase files only to test
targets.
'#import' the header files of each custom class that you use in a test case.

How to set up working logic unit tests target in Xcode 4.5 "Command Line Tool" project?

Having trouble getting unit tests set up for a specific scenario. Here's what I'm trying:
In Xcode 4.5, I created a simple OSX "Command Line Tool" application project (Foundation).
Note that Xcode does not provide the option to add unit tests to a "Command Line Tool" project automatically – so please don't suggest ticking the ticky-box; it ain't there :-/
In my project, I created a trivial example class that I'd like to test; e.g. "Shape".
I followed instructions in Apple's Xcode Unit Testing Guide for Setting Up Unit-Testing in a Project:
I added a unit test target to my project, and
I edited the "Test" scheme to run the tests in the new target.
In the test project's implementation (.m) file, I added an import for Shape.h and code in the setUp() method to instantiate a shape and assign it to an instance variable.
At that point, I decided to see if things would build and if the default test would run still. However, when I selected Product...Test from the menu, the build failed with the following error:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_Shape", referenced from:
objc-class-ref in ExampleTests.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Interpreting this error is not the issue. I grok that the unit test target isn't being linked to the binary containing Shape's implementation. However, I don't (yet) grok Xcode unit testing & target configuration. So:
What do I need to do in order to get the test target linking against the command line tool's resulting output? Can I link to a command-line executable from the unit test target? Apple's documentation looks specific to regular OSX applications (*.app) and iOS applications, and this is neither.
I have business logic classes that I'd like to develop in a command-line tool setting (to begin with), so I'd like to understand what I need to do to get a unit test target running in a "Command Line Tool" type of project. Thank you!
(p.s. Note that I'm not interested in running my unit tests from the command line – Stack Overflow already suggested "similar" questions on how to do that – but rather running unit tests on a "Command Line Tool" type project, and still from within Xcode.)
I've determined a workaround I find suitable and which doesn't appear to have significant drawbacks, other than an added target.
In a nutshell: the solution involves adding a static library target to take advantage of Xcode's ability to create & run unit test code around such a target. The command line tool target then delegates to the static library, where an alternate main()-like function is defined and called by the real main() entry point. The command line tool is kept free of non-trivial code, so the unit test target can access everything worth testing.
Here are the steps:
From an empty Xcode, from the menu choose File...New Project.
In the resulting dialog, chose OS X...Application...Command Line Tool. For the purpose of this example, I'll assume it is named SampleCmd.
After the basic command line tool project has been created:
From the menu choose File...New...Target.
In the resulting dialog, choose OS X...Framework & Library...Cocoa Library. For the purpose of this example, I'll assume it is named SampleCmdLogic.
Choose type Static, so the command line tool will remain a stand-alone executable.
Make sure the Include Unit Tests box is checked.
After the static library project has been created:
Copy the main() function from main.m to SampleCmdLogic.m, replacing the #implementation block. (This file will hold the main entry point only. Other files can be added for Objective-C classes, etc.) Rename the function to libMain().
In SampleCmdLogic.h, add a declaration for the new libMain(), replacing the #interface block:
int libMain(int argc, const char * argv[]);
In the command line tool's main.m, add an #import "SampleCmdLogic.h" at top.
In the command line tool's main.m, change the entire contents of the real main() function to:
return libMain(argc, argv);
The code is now ready, but there are required linking steps:
In the project settings for SampleCmd, under Build Phases, expand Target Dependencies and add (+) SampleCmdLogic as a dependency.
In the project settings for SampleCmd, under Build Phases, expand Link Binary With Libraries and add (+) libSampleCmdLogic.a
Everything is now ready. When you change to the SampleCmd target and choose Product..Run from the menu, the build should succeed and output generated as expected. When you change to the SampleCmdLogic target and choose Product...Test from the menu, the build should succeed and the unit tests will run. The single issue reported will be the initial default failing unit test assertion inserted by Xcode, in SampleCmdLogicTests.m. This is expected.
From this point on, proceed to add all logic and corresponding tests to the SampleCmdLogic target. The SampleCmd target is to remain trivial and providing the command line tool entry point only.
There are normally a number of additional steps to add a test target to an app project -- in particular, the setting of Bundle Loader and Test Host as I describe in https://stackoverflow.com/a/12624873/246895.
But when I did them with a Command Line Tool and tried running tests, all it did was run the tool. For an app, it goes through a phase of launching the app, injecting the test bundle into the running app, then executing the tests. But these phases don't apply to Command Line Tools.
So instead of an injected test bundle, what you'll need is a second command-line tool that runs your tests. Then set the your classes so they target the test tool as well as your actual tool. gh-unit and google-toolbox-for-mac both follow this model, so I'd try them.
It seems like the most immediate problem is simply that Shape is not included in the new test target. Try adding Shape.m to the test target:
Click on Shape.m in Project Navigator
Open the Utilities view (View -> Utilities -> Show Utilities)
In the Target Membership section of the Utilities view, make sure that both your app and test targets are checked.
I don't know if that's the end of your issues with your setup, but it seems like a likely candidate for your most immediate problem.

Xcode 3.1.3 problems unit-testing a plug-in

Following Chris Hanson's blogs and Apple's Automated Unit Testing with Xcode 3 and Objective-C I have started implementing unit tests for my projects. However, I use a lot of plug-ins (loadable bundles) and I can't quite figure out how to test them.
I figured I would use the approach Chris Hanson described for testing frameworks.
I started with a Cocoa Bundle project, added a principal class and changed the type to plugin.
Then I added the unit test bundle, add the plugin as a direct dependency (Apple's instructions) and set the following build settings:
Bundle Loader: $(BUILT_PRODUCTS_DIR)/CocoaPlugin.plugin/Contents/MacOS/CocoaPlugin
Test Host: $(BUNDLE_LOADER)
The problem is that as soon as I've done that and build the test target, I get this message:
error: Test host '/Users/elisevanlooij/Documents/Plug-ins/CocoaPlugin/build/DebugCocoaPlugin.plugin/Contents/MacOS/CocoaPlugin' exited abnormally with code 127 (it may have crashed). [code 126 in another plugin]
I had hoped that adding the otest custom executable would help, but unfortunately not. I really hope someone can help because not being able to unit test my plugin really puts a cramp in my testing lifestyle.
Take a step back. Your Bundle Loader setting is erroneous and adding a custom executable is not going to affect compilation of a unit-test bundle.
You need to get your unit-test bundle to build without errors (and warnings!), and your tests will run automatically (you do have at least one valid SenTestCase class with at least one valid test method, right?).
So,
are you saying that your test-bundle compiles without warnings and you have written some tests using classes and methods from your plugin? If so you must have some how taken care of loading the plugin-bundle into the unit-test-bundle and defining some kind of API, as the plugin-bundle doesn't have any public headers, right?
see Apple docs here
Loading plugins into plugins (essentially what you are trying to do) is not easy and they are not magically 'linked' at compile time like the frameworks in the Chris Hanson Blog that you refer too. They wouldn't be plugins if they were.
The simplest way to go is to not actually test your plugin at all but add the files you want to test directly to the unit-test bundle. At least this way you can get on with testing your code without fiddling about with dynamically loading bundles.
But if this isn't satisfactory, you can get what you are trying to do to work with a little effort - you should definitely add tests to verify that your plugin is loaded and that the symbols you think are available REALLY are available. Once your tests build ok you should follow Chris Hanson's other excellent blog on debugging unit test bundles showing you how to step thru your tests in the debugger - you should be able to track down any errors.
Your
Bundle Loader: $(BUILT_PRODUCTS_DIR)/CocoaPlugin.plugin/Contents/MacOS/CocoaPlugin
is correct. It means that when
linking your test bundle you do not include the classes under test there, and they will
be looked up from CocoaPlugin. It is a compile time setting and should cause your test
bundle to compile/link sucesfully. (See -bundle_loader in man ld)
Your
Test Host: $(BUNDLE_LOADER)
is incorrect. Your test host should be either an application (with a NSApplicationMain called from main method) or not set. This TEST_HOST setting is a runtime setting to run your unit tests. You basically have two options:
Do not set TEST_HOST, and load your plugin from your test bundle. For example you can do this using the initlaize method.
Create a dummy test_host application that will load your plugin, and then call NSApplicationMain, and use this app as your TEST_HOST.
The +initalize method for your test bundle to load the plugin would look like this:
+ (void)initialize
{
NSBundle* bundle = [NSBundle bundleWithPath:pathToPlugin];
[bundle load];
NSLog(#"Loaded:%#\n",bundle);
}
The main method in your dummy_test host app can look like this:
int main(int argc,const char** argv)
{
NSBundle* bundle = [NSBundle bundleWithPath:pathToPlugin];
[bundle load];
NSLog(#"Loaded:%#\n",bundle);
return NSApplicationMain(argc,argv);
}
Other ideas for testing plugins:
use an independent bundle: Do not specify either BUNDLE_LOADER or TEST_HOST and put your classes from the plugin also into the unittest bundle.
put your test cases into the plugin, and try to get that unittest. Just weak link SenTestingKit to your plugin and add a script phase with: TEST_RIG=/Developer/Tools/otest "${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests".

Resources