-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Wish for a way to test non-public APIs #31775
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
@amirh, would it help here if dartdoc didn't document symbols that were marked (/cc @jcollins-g) |
Not documenting things doesn't stop people from relying on them -- if anything, it encourages people to depend on them, because they think they're using Secret APIs and those are "cool". |
Sounds like a language issue to me. Basically "library private" and "public" are not sufficient for visibility controls. |
What is b/71583889? |
Sounds like #28066 ( Or rather, a language-enforced version of #28066. At the language level, this will take many moons to spec, discuss, implement, and release, but I imagine |
Many of our developers don't run the analyzer. The analyzer does not stop people from depending on APIs. |
I think it would be interesting to do the following work:
Once we get to this point, the From spending a bit of time researching this, I think we need to do the following:
In Dart 2.0, all code will effectively run through the new CFE (common front-end), and it's not unreasonable to see if we can move the visibility restriction code to the CFE instead of only in the analyzer. Another option would be the Flutter CLI enforcing certain analysis checks. |
(One of the hardest issues here is that the language has no concept of what a "package" is, it's a pub-only convention, and even there the various folders and conventions are quite loose and not well documented or enforced) |
I thought that 'library' was well defined by the language. Is there some question about what it means? |
I have no opinion on the above so long as the performance is good. The analyzer has historically not been fast enough to really be something we'd consider putting on the critical path (hot reload times are measured in milliseconds). |
It is, but the addition of part files, and the fact there isn't any "true" private (private to the class, for example), makes it not a direct replacement for
The CFE is going to have to be, so if we accept that premise it should be fast enough to enforce access control. I imagine the analyzer could be faster and incremental if on-disk caching was used. |
We also care about cold first-launch performance (though that's measured in seconds, not milliseconds), since that's the first experience our users will have. But yeah, if the analyzer gets much faster, or if a subset of the analyzer can be placed in the critical path without a performance penalty, then that seems like a fine way to extend the language as far as I'm concerned. |
I don't know the gritty details of the Flutter CLI, but when I use it for the first time on a new project it takes quite a while to prepare Maven/XCode/etc (lots of non-Dart dependencies). During this time couldn't the analyzer cache summaries of dependencies (similar to how Bazel/Blaze does it), given that those are unlikely to change? It would make even "first" analysis pretty fast, and incremental even faster. /cc @devoncarew |
There are many things we could do to make performance fast, and we should do many of them. :-) |
A hack that might not be relevant for Flutter: if you know the name of a public static method of a library private class, then it can be accessed from outside the library using mirrors. |
One similar trick we use a lot in Flutter is to have a private class, obtain an instance of that class through various shenanigans, and then cast it to "dynamic" and call the non-underscore-prefixed members of that class directly. |
The usual approach is to put private things in separate libraries /lib/src/ and thereby not promise not to break it. If you really want to access it, you can, but it's painfully obvious that you are accessing internal stuff that isn't part of the public API. That's not a hard restriction, someone can choose to use those libraries and only get burned when they change. I'm not opposed to making some parts of packages "package private", say anything with a path part staring with That doesn't help with testing because the test files are typically not in the library they test, they live on the side. That's an approach for "package privacy" ( Any analysis-based access restriction on objects can be avoided by using dynamic access, so it will not actually prevent access by a determined user. Either the method is accessible, or it's not. I find that class-based restrictions (the normal class based "private" and "protected" from Java) doesn't work very well in Dart. I would personally prefer instance-based restrictions. A private field can only be accessed from an instance member of the object itself (that is, only through If we ever put something like that into the language, in a language with dynamic access, that would be the path of least resistance. Then private and protected members could be name-mangled, and only the statically detected accesses will work. Casting an object to dynamic and then trying to access a protected member will not succeed. |
Just for clarity, the analysis server is able to respond to deltas in tens of milliseconds (code completion, recalculated errors and warnings, ...), and hot reload is measured in hundreds of milliseconds. The analysis server is plenty fast enough for incremental changes and has been since the analysis driver work, cira Q4 2016. We do need to port flutter analyze over to being analysis server based in order to take advantage of the driver and cached analysis summaries. |
In practice, our customers will not put up with us breaking their code, regardless of whether we're breaking them by changing a public API or a private API they happen to rely on. As a result, we consider any publicly-accessible member to be part of our public API, and thus export all of it; as a rule we have no publicly-available API that we don't export and document.
In my experience, this is a best-case scenario. I frequently see the analyzer server takes tens of seconds to process a change. |
👍 . This comes up a lot in rolling packages in google3. Unless we have enforced unified tooling with access/visibility modifiers, a convention or "hint"-based annotation is not enough to consider this issue resolved. |
@matanlurey +1 to specing this out. While I'm sure not as badly as pub, dartdoc feels the pain of this not being specified as it has to make decisions about symbol public/private status, and encode these ad-hoc conventions. |
I will add, we could always make it some sort of unofficial specification if the language team (@leafpetersen @lrhn) can't make a decision yet, but definitely having analyzer, pub, dartdoc all make their own decisions isn't the way to go. |
In terms of @amirh's original request here, I think the simplest solution would be friend libraries, and some of @matanlurey's other comments. So you might have: library flutter.material.animated_icons;
friend '../../test/material/animated_icons_test.dart';
int _i = 7;
class A {
int _j = 9;
}
|
Seems reasonable. We could even go further and replace the "friend" keyword with "test". |
Re: friend libraries There was a recent request to broaden |
Duplicate of #1882 |
How about this: the |
Flutter treats all public identifiers as part of its public API and strives to guarantee backward compatibility for every public identifier that is introduced to the framework.
As private identifiers (e.g "_foo") are only accessible from the same library they are typically not accessible from tests which are a different library.
This means that every identifier that we want to directly use in a test has to be public, and we need to commit for always keeping the framework compatible with it.
Current workaround (using library parts)
One example where this made us use a dirty workaround is the animated_icons library: We need some support of vector animations, but we did not yet have the time to properly design the API and format for it.
We wanted to implement vector animation support, as an internal implementation detail, without committing to keep supporting the current API.
But we did want to test the implementation.
What we ended up doing is implementing an animated_icons library that is made of parts, and re-define the same library using the same parts in the test file so it can access the private identifiers.
This workaround is not ideal as:
The text was updated successfully, but these errors were encountered: