Verifies that there are no guarded methods currently pending (see guard).
If a guarded method is currently pending, and this is not a call nested from inside that method's body (directly or indirectly), then this method will throw a detailed exception.
Source
static void guardSync() { if (_scopeStack.isEmpty) { // No scopes open, so we must be fine. return; } // Find the current TestAsyncUtils scope zone so we can see if it's the one we expect. final Zone zone = _currentScopeZone; if (zone == _scopeStack.last.zone) { // We're still in the current scope zone. All good. return; } // If we get here, we know we've got a conflict on our hands. // We got an async barrier, but the current zone isn't the last scope that // we pushed on the stack. // Find which scope the conflict happened in, so that we know // which stack trace to report the conflict as starting from. // // For example, if we called an async method A, which ran its body in a // guarded block, and in its body it ran an async method B, which ran its // body in a guarded block, but we didn't await B, then in A's block we ran // an async method C, which ran its body in a guarded block, then we should // complain about the call to B then the call to C. BUT. If we called an async // method A, which ran its body in a guarded block, and in its body it ran // an async method B, which ran its body in a guarded block, but we didn't // await A, and then at the top level we called a method D, then we should // complain about the call to A then the call to D. // // In both examples, the scope stack would have two scopes. In the first // example, the current zone would be the zone of the _scopeStack[0] scope, // and we would want to show _scopeStack[1]'s creationStack. In the second // example, the current zone would not be in the _scopeStack, and we would // want to show _scopeStack[0]'s creationStack. int skipCount = 0; _AsyncScope candidateScope = _scopeStack.last; _AsyncScope scope; do { skipCount += 1; scope = candidateScope; if (skipCount >= _scopeStack.length) { if (zone == null) break; // Some people have reported reaching this point, but it's not clear // why. For now, just silently return. // TODO(ianh): If we ever get a test case that shows how we reach // this point, reduce it and report the error if there is one. return; } candidateScope = _scopeStack[_scopeStack.length - skipCount - 1]; assert(candidateScope != null); assert(candidateScope.zone != null); } while (candidateScope.zone != zone); assert(scope != null); StringBuffer message = new StringBuffer(); message.writeln('Guarded function conflict. You must use "await" with all Future-returning test APIs.'); final _StackEntry originalGuarder = _findResponsibleMethod(scope.creationStack, 'guard', message); final _StackEntry collidingGuarder = _findResponsibleMethod(StackTrace.current, 'guardSync', message); if (originalGuarder != null && collidingGuarder != null) { String originalName; if (originalGuarder.className == null) { originalName = '(${originalGuarder.methodName}) '; message.writeln( 'The guarded "${originalGuarder.methodName}" function ' 'was called from ${originalGuarder.callerFile} ' 'on line ${originalGuarder.callerLine}.' ); } else { originalName = '(${originalGuarder.className}.${originalGuarder.methodName}) '; message.writeln( 'The guarded method "${originalGuarder.methodName}" ' 'from class ${originalGuarder.className} ' 'was called from ${originalGuarder.callerFile} ' 'on line ${originalGuarder.callerLine}.' ); } final String again = (originalGuarder.callerFile == collidingGuarder.callerFile) && (originalGuarder.callerLine == collidingGuarder.callerLine) ? 'again ' : ''; String collidingName; if ((originalGuarder.className == collidingGuarder.className) && (originalGuarder.methodName == collidingGuarder.methodName)) { originalName = ''; collidingName = ''; message.writeln( 'Then, it ' 'was called ${again}from ${collidingGuarder.callerFile} ' 'on line ${collidingGuarder.callerLine}.' ); } else if (collidingGuarder.className == null) { collidingName = '(${collidingGuarder.methodName}) '; message.writeln( 'Then, the "${collidingGuarder.methodName}" function ' 'was called ${again}from ${collidingGuarder.callerFile} ' 'on line ${collidingGuarder.callerLine}.' ); } else { collidingName = '(${collidingGuarder.className}.${collidingGuarder.methodName}) '; message.writeln( 'Then, the "${collidingGuarder.methodName}" method ' '${originalGuarder.className == collidingGuarder.className ? "(also from class ${collidingGuarder.className})" : "from class ${collidingGuarder.className}"} ' 'was called ${again}from ${collidingGuarder.callerFile} ' 'on line ${collidingGuarder.callerLine}.' ); } message.writeln( 'The first ${originalGuarder.className == null ? "function" : "method"} $originalName' 'had not yet finished executing at the time that ' 'the second ${collidingGuarder.className == null ? "function" : "method"} $collidingName' 'was called. Since both are guarded, and the second was not a nested call inside the first, the ' 'first must complete its execution before the second can be called. Typically, this is achieved by ' 'putting an "await" statement in front of the call to the first.' ); if (collidingGuarder.className == null && collidingGuarder.methodName == 'expect') { message.writeln( 'If you are confident that all test APIs are being called using "await", and ' 'this expect() call is not being called at the top level but is itself being ' 'called from some sort of callback registered before the ${originalGuarder.methodName} ' 'method was called, then consider using expectSync() instead.' ); } message.writeln( '\n' 'When the first ${originalGuarder.className == null ? "function" : "method"} ' '$originalName' 'was called, this was the stack:' ); message.writeln(FlutterError.defaultStackFilter(scope.creationStack.toString().trimRight().split('\n')).join('\n')); } throw new FlutterError(message.toString().trimRight()); }