Skip to content

Commit ea12e7e

Browse files
committed
test(symbols): Track the symbols we export from the angular library
Closes dart-archive#398
1 parent 613030a commit ea12e7e

File tree

1 file changed

+286
-0
lines changed

1 file changed

+286
-0
lines changed

test/angular_spec.dart

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ library angular_spec;
22

33
import '_specs.dart';
44
import 'package:angular/utils.dart';
5+
import 'dart:mirrors';
56

67
main() {
78
describe('angular.dart unittests', () {
@@ -40,4 +41,289 @@ main() {
4041
}).toThrow('Unknown function type, expecting 0 to 5 args.');
4142
});
4243
});
44+
45+
describe('angular symbols', () {
46+
it('should not export symbols that we do not know about', () {
47+
// Test is failing? Add new symbols to the "ALLOWED_NAMES" list below.
48+
// But make sure that you intend to export the symbol!
49+
// Questions? Talk to @jbdeboer
50+
51+
_getSymbolsFromLibrary(String libraryName) {
52+
var names = [];
53+
var SYMBOL_NAME = new RegExp('"(.*)"');
54+
_unwrapSymbol(sym) => SYMBOL_NAME.firstMatch(sym.toString()).group(1);
55+
56+
var seen = {};
57+
58+
// TODO(deboer): Add types once Dart VM 1.2 is deprecated.
59+
extractSymbols(/* LibraryMirror */ lib) {
60+
lib.declarations.forEach((symbol, _) {
61+
names.add([_unwrapSymbol(symbol), _unwrapSymbol(lib.qualifiedName)]);
62+
});
63+
64+
lib.libraryDependencies.forEach((/* LibraryDependencyMirror */ libDep) {
65+
LibraryMirror target = libDep.targetLibrary;
66+
if (!libDep.isExport) return;
67+
if (!seen.containsKey(target)) {
68+
seen[target] = true;
69+
extractSymbols(target);
70+
}
71+
});
72+
};
73+
74+
var lib = currentMirrorSystem().findLibrary(new Symbol(libraryName));
75+
76+
extractSymbols(lib);
77+
return names;
78+
}
79+
80+
var names;
81+
try { // Not impleneted in Dart VM 1.2
82+
names = _getSymbolsFromLibrary("angular");
83+
} on UnimplementedError catch (e) {
84+
return; // Not implemented, quietly skip.
85+
} catch (e) {
86+
print("Error: $e");
87+
return; // On VMes <1.2, quietly skip.
88+
}
89+
90+
var ALLOWED_PREFIXS = [
91+
"Ng",
92+
"ng",
93+
"Angular",
94+
"_"
95+
];
96+
97+
// NOTE(deboer): There are a number of symbols that should not be
98+
// exported in the list. We are working on un-export the symbols.
99+
// Comments on each symbols below.
100+
var ALLOWED_NAMES = [
101+
"di.FactoryFn",
102+
"di.Injector",
103+
"di.InvalidBindingError",
104+
"di.Key", // common name, should be removed.
105+
"di.TypeFactory",
106+
"di.Visibility",
107+
"di.NoProviderError",
108+
"di.CircularDependencyError",
109+
"di.ObjectFactory",
110+
"di.Module",
111+
"angular.core.AnnotationMap",
112+
"angular.core.LruCache", // internal?
113+
"angular.core.ScopeStats",
114+
"angular.core.ArrayFn", // internal?
115+
"angular.core.Interpolation",
116+
"angular.core.LongStackTrace", // internal?
117+
"angular.core.Cache", // internal?
118+
"angular.core.ExpressionVisitor", // internal?
119+
"angular.core.ScopeEvent",
120+
"angular.core.MapFn", // internal?
121+
"angular.core.EvalFunction1", // internal?
122+
"angular.core.MetadataExtractor", // internal?
123+
"angular.core.ExceptionHandler",
124+
"angular.core.ZoneOnTurn", // internal?
125+
"angular.core.ZoneOnError", // internal?
126+
"angular.core.ScopeDigestTTL",
127+
"angular.core.EvalFunction0", // internal?
128+
"angular.core.AnnotationsMap", // internal?
129+
"angular.core.RootScope",
130+
"angular.core.CacheStats",
131+
"angular.core.ScopeLocals",
132+
"angular.core.ScopeStreamSubscription",
133+
"angular.core.Interpolate",
134+
"angular.core.NOT_IMPLEMENTED", // internal?
135+
"angular.core.Scope",
136+
"angular.core.AttrFieldAnnotation",
137+
"angular.core.UnboundedCache", // internal?
138+
"angular.core.ScopeStream", // internal?
139+
"angular.core.FilterMap", // internal?
140+
"angular.core.AstParser", // internal?
141+
"angular.watch_group.FunctionApply", // internal?
142+
"angular.watch_group.WatchGroup", // internal?
143+
"angular.watch_group.ContextReferenceAST", // internal?
144+
"angular.watch_group.ConstantAST", // internal?
145+
"angular.watch_group.Watch",
146+
"angular.watch_group.ReactionFn", // internal?
147+
"angular.watch_group.ChangeLog",
148+
"angular.watch_group.FieldReadAST", // internal?
149+
"angular.watch_group.PureFunctionAST", // internal?
150+
"angular.watch_group.PrototypeMap", // internal?
151+
"angular.watch_group.CollectionAST", // internal?
152+
"angular.watch_group.MethodAST", // internal?
153+
"angular.watch_group.AST", // internal?
154+
"angular.watch_group.RootWatchGroup",
155+
"angular.core.dom.AnimationResult",
156+
"angular.core.dom.WalkingViewFactory", // internal?
157+
"angular.core.dom.ResponseError",
158+
"angular.core.dom.View",
159+
"angular.core.dom.ElementBinder", // internal?
160+
"angular.core.dom.NoOpAnimation",
161+
"angular.core.dom.AttributeChanged",
162+
"angular.core.dom.HttpBackend",
163+
"angular.core.dom.HttpDefaults",
164+
"angular.core.dom.TaggedElementBinder", // internal?
165+
"angular.core.dom.LocationWrapper",
166+
"angular.core.dom.Cookies",
167+
"angular.core.dom.ElementBinderTreeRef", // internal?
168+
"angular.core.dom.EventHandler",
169+
"angular.core.dom.Response",
170+
"angular.core.dom.HttpDefaultHeaders",
171+
"angular.core.dom.Animation",
172+
"angular.core.dom.ViewPort",
173+
"angular.core.dom.TemplateLoader",
174+
"angular.core.dom.RequestErrorInterceptor",
175+
"angular.core.dom.TaggedTextBinder", // internal?
176+
"angular.core.dom.Http",
177+
"angular.core.dom.BoundViewFactory", // internal?
178+
"angular.core.dom.ElementBinderFactory", // internal?
179+
"angular.core.dom.DirectiveMap", // internal?
180+
"angular.core.dom.BrowserCookies",
181+
"angular.core.dom.HttpInterceptor",
182+
"angular.core.dom.cloneElements", // internal?
183+
"angular.core.dom.EventFunction", // internal?
184+
"angular.core.dom.RequestInterceptor",
185+
"angular.core.dom.DefaultTransformDataHttpInterceptor",
186+
"angular.core.dom.HttpResponseConfig",
187+
"angular.core.dom.ElementProbe",
188+
"angular.core.dom.ApplyMapping", // internal?
189+
"angular.core.dom.ViewCache", // internal?
190+
"angular.core.dom.FieldMetadataExtractor", // internal?
191+
"angular.core.dom.Compiler",
192+
"angular.core.dom.HttpResponse",
193+
"angular.core.dom.UrlRewriter",
194+
"angular.core.dom.DirectiveRef",
195+
"angular.core.dom.HttpInterceptors",
196+
"angular.core.dom.forceNewDirectivesAndFilters", // internal?
197+
"angular.core.dom.DirectiveSelectorFactory", // internal?
198+
"angular.core.dom.ObserverChanged",
199+
"angular.core.dom.TaggingViewFactory", // internal?
200+
"angular.core.dom.NodeCursor", // internal?
201+
"angular.core.dom.TemplateCache", // internal?
202+
"angular.core.dom.ViewFactory",
203+
"angular.core.dom.NullTreeSanitizer",
204+
"angular.core.dom.NodeAttrs",
205+
"angular.core.dom.ElementBinderTree", // internal?
206+
"angular.core.dom.WalkingCompiler", // internal?
207+
"angular.core.dom.TaggingCompiler", // internal?
208+
"angular.core.dom.DirectiveSelector", // internal?
209+
"angular.core.parser.BoundGetter", // internal?
210+
"angular.core.parser.LocalsWrapper", // internal?
211+
"angular.core.parser.Getter", // common name
212+
"angular.core.parser.Parser",
213+
"angular.core.parser.ParserBackend",
214+
"angular.core.parser.BoundSetter",
215+
"angular.core.parser.Setter", // common name
216+
"angular.core.parser.syntax.LiteralObject", // evenything in syntax should be private
217+
"angular.core.parser.syntax.CallMember",
218+
"angular.core.parser.syntax.Filter",
219+
"angular.core.parser.syntax.defaultFilterMap",
220+
"angular.core.parser.syntax.BoundExpression",
221+
"angular.core.parser.syntax.AccessMember",
222+
"angular.core.parser.syntax.Expression",
223+
"angular.core.parser.syntax.AccessScope",
224+
"angular.core.parser.syntax.Assign",
225+
"angular.core.parser.syntax.AccessKeyed",
226+
"angular.core.parser.syntax.CallScope",
227+
"angular.core.parser.syntax.CallFunction",
228+
"angular.core.parser.syntax.Conditional",
229+
"angular.core.parser.syntax.Binary",
230+
"angular.core.parser.syntax.Chain",
231+
"angular.core.parser.syntax.Prefix",
232+
"angular.core.parser.syntax.Literal",
233+
"angular.core.parser.syntax.LiteralString",
234+
"angular.core.parser.syntax.LiteralArray",
235+
"angular.core.parser.syntax.LiteralPrimitive",
236+
"angular.core.parser.syntax.Visitor",
237+
"angular.core.parser.dynamic_parser.DynamicExpression",
238+
"angular.core.parser.dynamic_parser.ClosureMap",
239+
"angular.core.parser.dynamic_parser.DynamicParser",
240+
"angular.core.parser.dynamic_parser.DynamicParserBackend",
241+
"angular.core.parser.static_parser.StaticParserFunctions",
242+
"angular.core.parser.static_parser.StaticExpression",
243+
"angular.core.parser.static_parser.StaticParser",
244+
"angular.core.parser.lexer.Scanner", // everything in lexer should be private
245+
"angular.core.parser.lexer.OPERATORS",
246+
"angular.core.parser.lexer.NumberToken",
247+
"angular.core.parser.lexer.Token",
248+
"angular.core.parser.lexer.IdentifierToken",
249+
"angular.core.parser.lexer.StringToken",
250+
"angular.core.parser.lexer.CharacterToken",
251+
"angular.core.parser.lexer.Lexer",
252+
"angular.core.parser.lexer.KEYWORDS",
253+
"angular.core.parser.lexer.OperatorToken",
254+
"angular.directive.ItemEval",
255+
"angular.directive.OptionValueDirective",
256+
"angular.directive.InputSelectDirective",
257+
"angular.directive.InputTextLikeDirective",
258+
"angular.directive.InputNumberLikeDirective",
259+
"angular.directive.ContentEditableDirective",
260+
"angular.directive.InputCheckboxDirective",
261+
"angular.directive.InputRadioDirective",
262+
"angular.filter.JsonFilter",
263+
"angular.filter.Equals",
264+
"angular.filter.Mapper",
265+
"angular.filter.FilterFilter",
266+
"angular.filter.NumberFilter",
267+
"angular.filter.DateFilter",
268+
"angular.filter.LowercaseFilter",
269+
"angular.filter.UppercaseFilter",
270+
"angular.filter.OrderByFilter",
271+
"angular.filter.CurrencyFilter",
272+
"angular.filter.LimitToFilter",
273+
"angular.filter.Predicate",
274+
"angular.routing.RouteInitializerFn",
275+
"angular.routing.RouteProvider",
276+
"angular.routing.RouteInitializer",
277+
"angular.routing.RouteViewFactory",
278+
"route.client.RouteHandle",
279+
"route.client.RouteEnterEvent",
280+
"route.client.RouteStartEvent",
281+
"route.client.Router",
282+
"route.client.RouteEvent",
283+
"route.client.RouteLeaveEventHandler",
284+
"route.client.Route",
285+
"route.client.RouteImpl",
286+
"route.client.RouteLeaveEvent",
287+
"route.client.RoutePreEnterEventHandler",
288+
"route.client.RoutePreEnterEvent",
289+
"route.client.Routable",
290+
"route.client.RouteEnterEventHandler",
291+
"url_matcher.UrlMatcher",
292+
"url_matcher.UrlMatch",
293+
"dirty_checking_change_detector.FieldGetter", // everything in change detector should be private
294+
"dirty_checking_change_detector.DirtyCheckingChangeDetector",
295+
"dirty_checking_change_detector.DirtyCheckingRecord",
296+
"dirty_checking_change_detector.ItemRecord",
297+
"dirty_checking_change_detector.KeyValueRecord",
298+
"dirty_checking_change_detector.DuplicateMap",
299+
"dirty_checking_change_detector.GetterCache",
300+
"dirty_checking_change_detector.DirtyCheckingChangeDetectorGroup"
301+
];
302+
303+
var _nameMap = {};
304+
ALLOWED_NAMES.forEach((x) => _nameMap[x] = true);
305+
306+
assertSymbolNameIsOk(List nameInfo) {
307+
String name = nameInfo[0];
308+
String libName = nameInfo[1];
309+
310+
if (ALLOWED_PREFIXS.any((prefix) => name.startsWith(prefix))) return;
311+
312+
var key = "$libName.$name";
313+
if (_nameMap.containsKey(key)) {
314+
_nameMap[key] = false;
315+
return;
316+
}
317+
318+
throw "Symbol $key is exported thru the angular library, but it shouldn't be";
319+
};
320+
321+
names.forEach(assertSymbolNameIsOk);
322+
323+
// If there are keys that no longer need to be in the ALLOWED_NAMES list, complain.
324+
_nameMap.forEach((k,v) {
325+
if (v) print("angular_spec.dart: Unused ALLOWED_NAMES key $k");
326+
});
327+
});
328+
});
43329
}

0 commit comments

Comments
 (0)