Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added unit tests to the engine. #20216

Merged
merged 10 commits into from Aug 7, 2020
Merged

Conversation

gaaclarke
Copy link
Member

Description

Added tests to the Engine, previously there were none. The tests that were added were for the benefit of #20193

In order to make messages testable without incurring a performance cost I had to factor out one method to be templated so that we can delegate RuntimeController methods elsewhere.

Related Issues

#20193

Tests

EngineTest

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the contributor guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the C++, Objective-C, Java style guides for the engine.
  • I read the tree hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation.
  • All existing and new tests are passing.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them? Please read handling breaking changes.

@gaaclarke gaaclarke marked this pull request as ready for review August 4, 2020 00:48
@auto-assign auto-assign bot requested a review from GaryQian August 4, 2020 00:48
@gaaclarke gaaclarke removed the request for review from GaryQian August 4, 2020 00:54
/// `DispatchPlatformMessage` unless you are writing tests.
///
template <typename RuntimeControllerProxy>
void DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message,
Copy link
Member Author

@gaaclarke gaaclarke Aug 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might not scale well in the future, we'll see. It was this or make RuntimeController not final and give it virtual methods, which would have runtime costs. RuntimeController is very complicated to use in tests since it requires a dart snapshot and everything that goes along with that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also worth noting, I tried to make this method private but that doesn't scale well since googletest compiles each test as its own class that has to be friended. I can still do that if you want. It's not breaking any encapsulation having it public, it is a bit cleaner.

Copy link
Member

@chinmaygarde chinmaygarde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This OOLs a bunch of code and I agree with your hunch that this won't scale well. If the behavior of a the component is hard to test without templating, visibility or const correctness hacks, it usually means that that it has more than a single responsibility. That seems to be the case here.

Instead, I suggest isolating the message dispatcher into its own component (something like PlatformMessageDispatcher) and testing that separately from the engine. Then engine can then use an instance of this tested component. The implementation of this new object would just be a proc lookup in a map keyed by the channel name which is pretty much what is happening here. The test assertions can check the correctness of the entries in this map. For this, you don't need any of the Dart VM bits at all.

@gaaclarke
Copy link
Member Author

Thanks @chinmaygarde That approach has scaling issues as well. Right now engine.cc isn't tested and if every time we want to test it we have to split out the functionality to a separate class, that's not good either. Also, introducing PlatformMessageDispatcher or anything else to test in isolation may create an extra level of indirection (either through function pointers or virtual functions) which would have the performance cost we were trying to avoid by keeping RuntimeController's methods non-virtual. Let's chat about our options when you have a chance.

@flar
Copy link
Contributor

flar commented Aug 4, 2020

Trying to avoid a virtual function call per platform message dispatch sounds like it might be premature optimization to me. Are there statistics that show that these messages come in at high enough volume to measure these nanoseconds?

@gaaclarke
Copy link
Member Author

@flar It's an optimization, I wouldn't call it premature though. This is engine code in the place where messages are getting dispatched which can get some heavy usage. Virtual functions add just a couple of cycles to calls but more importantly they block optimizations like inlining. In isolation they don't matter much, but in aggregate across something like the engine it can make a big difference.

I'm fine making the methods virtual. I didn't think this was that bad of a tradeoff to get testability with no performance cost though. As we add more tests I'm skeptical this will last though and we'll probably have to migrate to virtual methods anyway.

Comment on lines 46 to 54
class MockRuntimeDelegate : public RuntimeDelegate {
public:
MOCK_METHOD(std::string, DefaultRouteName, (), (override));
MOCK_METHOD(void, ScheduleFrame, (bool), (override));
MOCK_METHOD(void, Render, (std::unique_ptr<flutter::LayerTree>), (override));
MOCK_METHOD(void,
UpdateSemantics,
(SemanticsNodeUpdates, CustomAccessibilityActionUpdates),
(override));
MOCK_METHOD(void,
HandlePlatformMessage,
(fml::RefPtr<PlatformMessage>),
(override));
MOCK_METHOD(FontCollection&, GetFontCollection, (), (override));
MOCK_METHOD(void,
UpdateIsolateDescription,
(const std::string, int64_t),
(override));
MOCK_METHOD(void, SetNeedsReportTimings, (bool), (override));

MOCK_METHOD(std::unique_ptr<std::vector<std::string>>,
ComputePlatformResolvedLocale,
(const std::vector<std::string>&),
(override));
};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could get rid of the need for this if we were willing to change the RuntimeControllers instance variable for its RuntimeDelegate from a reference to a pointer. I think thats a desirable change but I suspected it might be controversial.

@gaaclarke
Copy link
Member Author

@chinmaygarde @flar I moved to virtual methods in the RuntimeController =)

@gaaclarke
Copy link
Member Author

One sec, I'm still battling a bug in googletest on windows. I think if i used the deprecated methods MOCK_METHOD0 versus MOCK_METHOD it should work.

@flar
Copy link
Contributor

flar commented Aug 7, 2020

My "food for thought" comment above was the extent of my review, so consider the matter resolved if you took it under consideration and came up with an acceptable solution...

@gaaclarke
Copy link
Member Author

Landing before last CI test passes. This last test previously passed, the only change since then should be covered in other tests. When the test first ran it had an infra error. When it was rerun it got queued for over 2 hours. Attempts to launch it manually with led failed with "permission denied":

$ led get-builder 'luci.flutter.try:Mac Host Engine' 
E0807 15:52:40.850576   74654 fleetspeak.go:79] Error flushing fleetspeak data: (downstream) write of 13191 bytes (compressed payload) + metadata: Dial("/var/run/fleetspeak/clp_exporter.sock", "CL:324807037"): dial unix /var/run/fleetspeak/clp_exporter.sock: connect: permission denied
E0807 15:53:40.732003   74654 fleetspeak.go:79] Error flushing fleetspeak data: previous message still pending send: (downstream) write of 13191 bytes (compressed payload) + metadata: Dial("/var/run/fleetspeak/clp_exporter.sock", "CL:324807037"): dial unix /var/run/fleetspeak/clp_exporter.sock: connect: permission denied

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
4 participants