iOS UI 自动化:监控信号日志

iOS UI Automation: Monitor log for signals

我正在设置一个 Xcode/Instruments UI 自动化项目,目的是自动捕获我的应用程序在各种状态下的屏幕截图。

这种方法的主要问题之一是时间安排。因为应用程序与服务器通信,所以某些事件发生所需的时间从 运行 运行 变化(有时相当多)。使用延迟远非理想,因为它会增加执行屏幕截图捕获的时间(我们必须 运行 这大约 280 次,所以它会加起来)并且仍然不能保证应用程序在正确的状态(例如,我们不能保证 服务器将在 5 秒内 return,但大多数情况下应该 < 1 秒)。

所以我的想法是,一个理想的解决方案是将良性日志语句插入实际的应用程序本身,可以由 UI 自动化脚本监控。例如,只要脚本检测到“!!SCREENSHOT!!”在日志中,它可以捕捉另一个屏幕截图。通过这种方式,我们可以使用编程结构来确保应用程序处于截屏的正确状态,并通过避免延迟来减少脚本的整体执行时间。

我的问题首先是这可能吗?如果是这样,怎么办?如果没有,其他人是否有其他想法来解决这个问题?

好吧,我找不到任何关于从自动化脚本监视日志的信息,所以我用另一种方式解决了这个问题,目前看来效果很好。

基本思想是不使用日志作为信号,而是使用 UI 元素。我在我的应用程序委托中创建了一个简单的方法,它在屏幕外创建了一个空标签并为其分配了一个已知的 accessibilityIdentifierdispatch_after 并不是绝对必要的,但我想确保控制权已返回到主捕获前的事件循环)。

- (void)signalScreenshot:(int)number {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if ( ![[[NSProcessInfo processInfo] arguments] containsObject:@"automatedexecution"] ) {
            NSLog(@"Screenshot signaled, but ignoring because we are not in automated execution.");
            return;
        }
        UILabel* elem = [[UILabel alloc] initWithFrame:CGRectMake(-100, -100, 50, 50)];
        elem.accessibilityIdentifier = [NSString stringWithFormat:@"screenshotSignal_%d", number];
        NSLog(@"Signaling for screenshot %d...", number);
        [self.window insertSubview:elem atIndex:0];
    });
}

这个不可见元素在视图层次结构中的存在充当了自动化脚本捕捉屏幕截图的信号。在自动化脚本中,我编写了一个 captureScreen 函数,它使用自增计数器来识别屏幕编号。每个增量屏幕号都有一个唯一的信号元素。

var screenshotCounter = 0;

function captureScreen() {
    screenshotCounter++;
    var screenshotSignal = target.frontMostApp().mainWindow().elements().firstWithName("screenshotSignal_" + screenshotCounter);
    screenshotSignal.withValueForKey(1, "isVisible");

    if ( screenshotSignal.isValid() ) {
        UIALogger.logDebug( "Screenshot " + screenshotCounter + " signaled." );
        target.captureScreenWithName( "" + screenshotCounter );
        return true;
    } else {
        target.logElementTree();
        throw "Did not detect screenshot signal " + screenshotCounter;
    }
}

现在,在我的脚本中,我可以自动执行应用程序以将其传送到正确的位置并调用 captureScreen()。然后,当脚本准备好通过调用上面的 signalScreenshot: 方法捕获屏幕时,它会留给应用程序本身发出信号。效果很好,没有人为延迟!