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

Detox tests hang with pending items on dispatch queue #3358

Closed
DerrickAfrifa opened this issue Apr 27, 2022 · 10 comments
Closed

Detox tests hang with pending items on dispatch queue #3358

DerrickAfrifa opened this issue Apr 27, 2022 · 10 comments

Comments

@DerrickAfrifa
Copy link

DerrickAfrifa commented Apr 27, 2022

Description

I am writing e2e tests on Detox to test a Firebase app. It looks like the call to firebase.auth().signInWithPhoneNumber(number) dispatches some items on the dispatch queue but these items don't ever seem to be dequeued and therefore the tests cannot proceed. My hunch is that there is a network request being made by the sign in call that never resolves. Here is the log:
Here is the log:

12:46:14.209 detox[41991] INFO:  [APP_STATUS] The app is busy with the following tasks:
• There are 2 work items pending on the dispatch queue: "Main Queue (<OS_dispatch_queue_main: com.apple.main-thread>)".
• Run loop "Main Run Loop" is awake.

I have read through this troubleshooting guide and it looks like the operation is on the Main thread (native) and the issue is a waiting too much issue.

Is there a way to inspect the items on the dispatch queue to further understand what they are? I have tried running the /usr/bin/xcrun simctl spawn <device> log stream --level debug --style compact --predicate 'process == "myapp"' but I don't understand the output. If it is useful I can upload the logs.

I'm hoping I can post some logs of some sort and someone can help me to find the reason for the items on the dispatch queue.
I have no experience with native development so device system logs and Objective C/Swift code mean nothing to me.
Thanks

Your environment

Detox version: 19.4.2
React Native version: 0.67.4
Node version: v12.22.6
Device model: iPhone 11 Simulator
OS: iOS
Test-runner (select one): jest-circus

@DerrickAfrifa DerrickAfrifa changed the title Detox tests hang with pending items on dispatch queue but cause Detox tests hang with pending items on dispatch queue Apr 28, 2022
@stale
Copy link

stale bot commented May 30, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If you believe the issue is still relevant, please test on the latest Detox and report back.

Thank you for your contributions!

For more information on bots in this reporsitory, read this discussion.

@stale stale bot added the 🏚 stale label May 30, 2022
@d4vidi
Copy link
Collaborator

d4vidi commented May 31, 2022

Deep-inspecting the content of the queue is tricky. My advice is to try to eliminate parts of the app until things work and with that pin-point the source.

@github-actions
Copy link

This issue appears to be a general usage or support question.
In order to get help, please either ask a question on Stack Overflow with the detox tag, or simply join our Discord.
Feel free to post your Stack Overflow question here for more visibility! We'll take a look at it ASAP.
For more information about our policy on issues, refer to this discussion.

@glesperance
Copy link

glesperance commented Jul 15, 2022

I am having the exact same issue.

On our side, the interaction happening relates to:

  1. RNFirebase/Auth
  2. Detox
  3. Expo SDK >= 43 (using unimodules expo SDK=42 resolves the issue.)
  • Making the signInWithPhoneNumber call into a promise does not help.
  • ::Taking out the firebaseAuth().signInWithPhoneNumber() call out into another view reproduces the synchronization problem::
  • Pulling the problematic RNFB auth call further up the app chain always leads to the synchronisation problem.

I have inspected the RNFB/Auth implementation and it is rather simple. The blocking call ends up being:

// node_modules/@react-native-firebase/auth/ios/RNFBAuthModule.m:

RCT_EXPORT_METHOD(signInWithPhoneNumber
                  : (FIRApp *)firebaseApp
                  : (NSString *)phoneNumber
                  : (RCTPromiseResolveBlock)resolve
                  : (RCTPromiseRejectBlock)reject) {
  [[FIRPhoneAuthProvider providerWithAuth:[FIRAuth authWithApp:firebaseApp]]
      verifyPhoneNumber:phoneNumber
             UIDelegate:nil
             completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
               if (error) {
                 [self promiseRejectAuthException:reject error:error];
               } else {
                 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
                 [defaults setObject:verificationID forKey:@"authVerificationID"];
                 resolve(@{@"verificationId" : verificationID});
               }
             }];
}

which eventually makes it here:

// Pods/FirebaseAuth/FIRPhoneAuthProvider.m
- (void)internalVerifyPhoneNumber:(NSString *)phoneNumber
                       UIDelegate:(nullable id<FIRAuthUIDelegate>)UIDelegate
                       completion:(nullable FIRVerificationResultCallback)completion {
  if (!phoneNumber.length) {
    completion(nil, [FIRAuthErrorUtils missingPhoneNumberErrorWithMessage:nil]);
    return;
  }
  [_auth.notificationManager
      checkNotificationForwardingWithCallback:^(BOOL isNotificationBeingForwarded) {
        if (!isNotificationBeingForwarded) {
          completion(nil, [FIRAuthErrorUtils notificationNotForwardedError]);
          return;
        }
        FIRVerificationResultCallback callback =
            ^(NSString *_Nullable verificationID, NSError *_Nullable error) {
              if (completion) {
                completion(verificationID, error);
              }
            };
        [self verifyClientAndSendVerificationCodeToPhoneNumber:phoneNumber
                                   retryOnInvalidAppCredential:YES
                                                    UIDelegate:UIDelegate
                                                      callback:callback];
      }];
}

which calls into the problematic function:

- (void)checkNotificationForwardingWithCallback:(FIRAuthNotificationForwardingCallback)callback {
  if (_pendingCallbacks) {
    [_pendingCallbacks addObject:callback];
    return;
  }
  if (_hasCheckedNotificationForwarding) {
    callback(_isNotificationBeingForwarded);
    return;
  }
  _hasCheckedNotificationForwarding = YES;
  _pendingCallbacks =
      [[NSMutableArray<FIRAuthNotificationForwardingCallback> alloc] initWithObjects:callback, nil];
  dispatch_async(dispatch_get_main_queue(), ^{
    NSDictionary *proberNotification = @{
      kNotificationDataKey : @{
        kNotificationProberKey : @"This fake notification should be forwarded to Firebase Auth."
      }
    };
    if ([self->_application.delegate
            respondsToSelector:@selector(application:
                                   didReceiveRemoteNotification:fetchCompletionHandler:)]) {
      [self->_application.delegate application:self->_application
                  didReceiveRemoteNotification:proberNotification
                        fetchCompletionHandler:^(UIBackgroundFetchResult result){
                        }];
#if !TARGET_OS_TV
    } else if ([self->_application.delegate
                   respondsToSelector:@selector(application:didReceiveRemoteNotification:)]) {
// iOS 10 deprecation
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
      [self->_application.delegate application:self->_application
                  didReceiveRemoteNotification:proberNotification];
#pragma clang diagnostic pop
#endif
    } else {
      FIRLogWarning(kFIRLoggerAuth, @"I-AUT000015",
                    @"The UIApplicationDelegate must handle remote notification for phone number "
                    @"authentication to work.");
    }
  });
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeout * NSEC_PER_SEC)),
                 FIRAuthGlobalWorkQueue(), ^{
                   [self callBack];
                 });
}

I haven't fully dissected the above function but refactoring internalVerifyPhoneNumber such that it doesn't call checkNotificationForwardingWithCallback allows detox to progress.

@DerrickAfrifa could you tell me what versions of RNFirebase/Expo you are running?
@d4vidi would you have any pointers explaining why detox fails to synchronise when checkNotificationForwardingWithCallback is called?

Another mystery here is why is this bug related to updating expo to SDK 45...

@glesperance
Copy link

A PR is in progress (see above ^) for this to be fixed upstream, but in the meantime you can refer to this stack overflow answer for a fix.

@dooleyb1
Copy link

dooleyb1 commented Jan 3, 2023

@glesperance did the change you made above to react-native-firebase/messaging resolve the issue for you completely? I've just upgraded our react-native-firebase packages to latest (16.5.0) & also detox package to latest (20.1.1) and I am still encountering the following after a firebase auth sign in with phone number:

detox[12860] i The app is busy with the following tasks:
• Run loop "Main Run Loop" is awake.
• There are 2 work items pending on the dispatch queue: "Main Queue (<OS_dispatch_queue_main: com.apple.main-thread>)"

I've inspected the package source code in my node_modules and can see your change exists.

@blyszcz
Copy link

blyszcz commented Aug 2, 2023

Same here @dooleyb1, were you able to fix it?

@ricardomonterrosa
Copy link

some here, help!

@otsalex
Copy link

otsalex commented Aug 11, 2023

Same here!

@glesperance
Copy link

glesperance commented Aug 11, 2023

The fix / pr worked for our use case.

However, please keep in mind that any component incorrectly using the dispatch queue will cause the behaviour you are observing.

Our issue was with firebase-auth however each case can be different.
Detox is only complaining because it relies on the dispatch queue being properly and cleanly managed.

If you're not relying on firebase-auth or running the latest version this would indicate that your problem is elsewhere.

If that is the case your best course of action is to "try to eliminate parts of the app until things work and with that pinpoint the source." as @d4vidi pointed out.

Hope this helps.

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

No branches or pull requests

7 participants