|
1 | 1 | "use strict"; |
2 | 2 |
|
3 | 3 | import assert = require("assert"); |
| 4 | +import childProcess = require("child_process"); |
4 | 5 | import fs = require("fs"); |
5 | 6 | import mkdirp = require("mkdirp"); |
6 | 7 | import os = require("os"); |
@@ -676,6 +677,78 @@ const UpdateSync = "updateSync.js"; |
676 | 677 | const UpdateSync2x = "updateSync2x.js"; |
677 | 678 | const UpdateNotifyApplicationReadyConditional = "updateNARConditional.js"; |
678 | 679 |
|
| 680 | +////////////////////////////////////////////////////////////////////////////////////////// |
| 681 | +// Forward the app's native log into this run's output. By default a run log contains nothing |
| 682 | +// whatsoever from the native module, and there's no way to tell from a CI log which native |
| 683 | +// path a test took. |
| 684 | + |
| 685 | +let nativeLogProcess: childProcess.ChildProcess = null; |
| 686 | +let nativeLogBuffer = ""; |
| 687 | + |
| 688 | +function readIOSLogEvent(line: string): string { |
| 689 | + try { |
| 690 | + // `log stream` opens with a human-readable banner before the ndjson starts, so |
| 691 | + // anything unparseable is expected and simply skipped. CPLog also prefixes its |
| 692 | + // format string with a newline. |
| 693 | + return (JSON.parse(line).eventMessage || "").trim() || null; |
| 694 | + } catch (error) { |
| 695 | + return null; |
| 696 | + } |
| 697 | +} |
| 698 | + |
| 699 | +function startNativeLogForwarding(): void { |
| 700 | + const isIOS = TestUtil.readMochaCommandLineFlag("--ios"); |
| 701 | + const isAndroid = TestUtil.readMochaCommandLineFlag("--android"); |
| 702 | + if (nativeLogProcess || (!isIOS && !isAndroid)) { |
| 703 | + return; |
| 704 | + } |
| 705 | + |
| 706 | + // The two platforms filter differently. iOS filters on CodePush's own message prefix in |
| 707 | + // the log command itself, and emits one JSON object per line. Android filters by log tag: |
| 708 | + // "ReactNative" is shared with React Native's own logging, but a release build barely uses |
| 709 | + // it, so it's good enough. "-T 1" starts at the tail rather than replaying earlier runs' |
| 710 | + // output, without mutating device state the way "logcat -c" would. |
| 711 | + const commandArgs = isIOS |
| 712 | + ? ["simctl", "spawn", "booted", "log", "stream", "--style", "ndjson", "--predicate", "eventMessage CONTAINS \"[CodePush]\""] |
| 713 | + : ["logcat", "-v", "brief", "-T", "1", "ReactNative:D", "*:S"]; |
| 714 | + |
| 715 | + const logProcess = childProcess.spawn(isIOS ? "xcrun" : "adb", commandArgs, { stdio: ["ignore", "pipe", "ignore"] }); |
| 716 | + |
| 717 | + logProcess.stdout.setEncoding("utf8"); |
| 718 | + logProcess.stdout.on("data", (chunk: string) => { |
| 719 | + nativeLogBuffer += chunk; |
| 720 | + const lines = nativeLogBuffer.split("\n"); |
| 721 | + // The last element is either empty or a partial line still being written. |
| 722 | + nativeLogBuffer = lines.pop() || ""; |
| 723 | + |
| 724 | + lines.forEach((line) => { |
| 725 | + const message = isIOS ? readIOSLogEvent(line) : line.trim(); |
| 726 | + if (message) { |
| 727 | + console.log(`[NATIVE] ${message}`); |
| 728 | + } |
| 729 | + }); |
| 730 | + }); |
| 731 | + |
| 732 | + // Failing to stream logs must never fail the run, this is diagnostics only. The handler |
| 733 | + // is also required: an unhandled "error" event on a child process throws. |
| 734 | + logProcess.on("error", (error: Error) => { |
| 735 | + console.log(`[NATIVE] Could not stream native logs: ${error.message}`); |
| 736 | + }); |
| 737 | + |
| 738 | + nativeLogProcess = logProcess; |
| 739 | +} |
| 740 | + |
| 741 | +beforeEach(function () { |
| 742 | + startNativeLogForwarding(); |
| 743 | +}); |
| 744 | + |
| 745 | +after(function () { |
| 746 | + if (nativeLogProcess) { |
| 747 | + nativeLogProcess.kill(); |
| 748 | + nativeLogProcess = null; |
| 749 | + } |
| 750 | +}); |
| 751 | + |
679 | 752 | ////////////////////////////////////////////////////////////////////////////////////////// |
680 | 753 | // Collect iOS Simulator crash reports for failed tests (e.g. the app under test crashed and |
681 | 754 | // the harness just idled until the test timeout), so they can be uploaded as CI artifacts. |
|
0 commit comments