seanhenry / SwiftMockGeneratorForXcode

An Xcode extension (plugin) to generate Swift test doubles automatically.
MIT License
748 stars 47 forks source link

"Could not find a protocol on..." #16

Closed j531 closed 5 years ago

j531 commented 5 years ago

I'm trying to generate a Spy class in my test target. When I go to Editor > Mock Generator > Generate spy, I get a "Could not find a protocol on..." error. This actually happens if I try to generate the Spy anywhere outside of the file containing the protocol itself. As a result, my workflow involves switching to the file containing the protocol, generating the Spy class and then cut / pasting it to my test target.

For what it's worth, I've tried the extension on a newly created project and it works fine in any file or target. Is there any way I can get you more detailing debugging information?

Using Xcode 10 and Mock Generator v1-beta.16.

Cheers

seanhenry commented 5 years ago

Hi @j531

Thanks for raising this issue. The reason why this happens is because source kit cannot find the class or protocol that your spy inherits. The generator will, however, be able to resolve your class or protocol if it is in the same file because it uses a custom resolver for local item instead of source kit.

Essentially, the reason why source kit might not be able to find a type is due to its naive implementation in this project. It is only configured to find all files in the project directory and doesn't understand modules, frameworks, etc.

This means that source kit will not find types in files outside the project directory. It also unfortunately means that it will not find types which have a duplicately named item; e.g. the same named class in separate modules. It also won't find items in third party libraries/frameworks, including apple frameworks.

The reason source kit isn't configured yet is because it's complicated. See this previous issue #6 . I am working on a solution to this problem but it will not be completed any time soon.

I'm going to keep this issue open so others can see and track the progress here.

seanhenry commented 5 years ago

Also the reason behind #14

BohdanOrlov commented 5 years ago

@seanhenry thank you for the extension! Just wanted to add that this issue makes it much harder to use the extension since we have all mocks in a separate test support framework while protocols in a production framework. image

seanhenry commented 5 years ago

Hi @BohdanOrlov

Thanks for your comment. It looks to me like your project setup should work ok with how source kit is configured. Have you managed to generate any mocks in their own file?

@j531 same question. Have you ever managed to generate a mock in its own file in Xcode 10? And was it working with Xcode 9?

j531 commented 5 years ago

@seanhenry

I always have to generate the mock in the file where the protocol is defined. I've only tested this in Xcode 10 (I don't have Xcode 9 installed).

In a similar vein and possibly related to this issue, I've noticed that if ProtocolA inherits from ProtocolB and ProtocolB is in a different file, any mock generated (in the same file as ProtocolA) will only have implementations for the ProtocolA interface but nothing from ProtocolB.

seanhenry commented 5 years ago

@j531 @BohdanOrlov

I've uploaded a new release. Can you install that and see if it solves your issues?

If the new release doesn't fix your issues then deselect the 'Automatically detect project path' checkbox in the companion app and select the folder containing your module production code (not your root project directory). This will provide SourceKit with only the files contained in that folder which will make it more likely to succeed.

If that still doesn't solve your problem then open the Console app and filter the messages by 'SourceKit error'. Take a look at what errors you are getting to see if they yield any clues. (This will only work if you use the latest release).

I hope this helps to solve your issues!

j531 commented 5 years ago

@seanhenry

The new version is working fine now. I can generate mocks in a separate target / file to the file I am referencing in the mock class. It also adds all implementation for any parent protocols of the protocol I am mocking. Thanks for the update!

I'll wait for @BohdanOrlov to confirm before closing.

seanhenry commented 5 years ago

That’s great news! Thanks for letting me know.

BohdanOrlov commented 5 years ago

@seanhenry its works if I select a folder with only one module inside.

If I use "automatic detection" it doesn't work and I get an error in Console: [1:getCursorInfo:10319:431.6181] failed to create an ASTInvocation: error when parsing the compiler arguments

It would be amazing to have it working with multiple modules, since switching the folder manually is painful :)

seanhenry commented 5 years ago

Ok, cool. So it looks like that error comes from SourceKit and it seems to be complaining about the arguments that the generator is sending to it. The compiler arguments that get sent to SourceKit are "-jn" (where n is the number of cores in your machine) and then each .swift file in your project directory.

I've tried to reproduce this error but I haven't managed to yet. Can you open the companion app, with your project open in Xcode, and note down the directory that you see (should be your project root directory) and then run $ find <path-in-companion-app> -name ".swift" and see if there's anything odd that might cause a problem? I've looked at spaces in file/folder names and reduced permissions in files but they don't seem to cause an issue.

BohdanOrlov commented 5 years ago

@seanhenry I didn't find anything special about names, but by excluding some modules I could make it work for remaining modules. Unfortunately, I can't tell what exactly causing the issue, maybe its duplicate name of types or something else. Would it be possible for you to somehow provide more output when the companion app fails to find/parse swift files so I could try to work around the issue for our codebase?

seanhenry commented 5 years ago

That sounds good. Does this mean you’ve identified the problem folder(s)?

I can get you a version which logs more stuff. I can log the exact arguments that it sends to source kit? I probably can’t log in more detail than that for source kit though because I hand it off to SourceKitten at that point... do you have an idea of something else that would be useful for me to log?

I’m also thinking about adding a user managed list of ignored patterns where you can ignore certain files/folders. That would allow users to ignore their test modules for example. Does that sound useful?

BohdanOrlov commented 5 years ago

I know only that some modules(folders) cause the problem, but since they are now small, I don't know what exactly is the problem (which files/subfolders). So additional logging would help.

Logging anything you put into SourceKitten and get back from it could help.

"Managed list of ignored patterns" might be useful, depending on how the process works now (forgive my ignorance). If the search process is terminated as soon as something "unparsable" found, then yes, such list would be a great help, so that I could identify which files/folders to skip and use the plugin for the rest.

seanhenry commented 5 years ago

Hi @BohdanOrlov

I've uploaded a new release which logs everything that I send to SourceKitten.

To find the logs, open the Console app as before. You should now find a log which points you to the log file. E.g. "SourceKit request written to: /tmp/...". In that file you can see the full request that is sent to SourceKitten. This will only be logged if there is an error.

Let me know if you find anything.

BohdanOrlov commented 5 years ago

@seanhenry the latest release works for me! I can generate mock for protocols defined in different files and even from different modules.

Regarding logs: I see errors like this error: TEST SOURCE KIT ERROR is that some sub not removed or it actually comes from SourceKitten?

I think the issue can be closed unless someone else can reproduce it! Thank you!

seanhenry commented 5 years ago

That’s good to hear! The only change I made was to move the -jn argument into two arguments so maybe that was the issue 🤔

Thanks for your time in helping me fix it - it’s really appreciated!

I’ll remove that test error in the next release.