Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public class ProcessScreenshots {
private static final byte[] PNG_SIGNATURE = new byte[]{
(byte) 0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A
};
private static final int MAX_RETRIES = 5;
private static final long RETRY_DELAY_MS = 2000;

public static void main(String[] args) throws Exception {
Arguments arguments = Arguments.parse(args);
Expand Down Expand Up @@ -71,8 +73,8 @@ static Map<String, Object> buildResults(
}
} else {
try {
PNGImage actual = loadPng(actualPath);
PNGImage expected = loadPng(expectedPath);
PNGImage actual = loadPngWithRetry(actualPath);
PNGImage expected = loadPngWithRetry(expectedPath);
Map<String, Object> outcome = compareImages(expected, actual);
if (Boolean.TRUE.equals(outcome.get("equal"))) {
record.put("status", "equal");
Expand Down Expand Up @@ -107,7 +109,7 @@ private static CommentPayload loadPreviewOrBuild(String testName, Path actualPat
return external;
}
}
PNGImage image = cached != null ? cached : loadPng(actualPath);
PNGImage image = cached != null ? cached : loadPngWithRetry(actualPath);
return buildCommentPayload(image, MAX_COMMENT_BASE64);
}

Expand Down Expand Up @@ -331,6 +333,58 @@ private static Map<String, Object> compareImages(PNGImage expected, PNGImage act
return result;
}

private static PNGImage loadPngWithRetry(Path path) throws IOException {
int attempt = 0;
long lastSize = -1;
while (true) {
try {
// Stabilize check: if file size is changing, wait
if (Files.exists(path)) {
long size = Files.size(path);
if (size != lastSize) {
lastSize = size;
if (attempt > 0) {
// If size changed, we should wait and retry
Thread.sleep(RETRY_DELAY_MS);
attempt++;
if (attempt >= MAX_RETRIES) {
break; // fall through to try loading anyway, will likely fail
}
continue;
}
}
}

return loadPng(path);
} catch (IOException e) {
// Only retry on truncated chunk or premature end of file
if (e.getMessage() != null &&
(e.getMessage().contains("PNG chunk truncated") ||
e.getMessage().contains("Premature end of file") ||
e.getMessage().contains("Missing IHDR"))) {

attempt++;
if (attempt >= MAX_RETRIES) {
throw e;
}
try {
System.err.println("Retrying load of " + path + " (attempt " + (attempt + 1) + "/" + MAX_RETRIES + "): " + e.getMessage());
Thread.sleep(RETRY_DELAY_MS);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted while waiting to retry load of " + path, ie);
}
} else {
throw e;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted while loading " + path, e);
}
}
return loadPng(path); // Final attempt
}

private static PNGImage loadPng(Path path) throws IOException {
byte[] data = Files.readAllBytes(path);
for (int i = 0; i < PNG_SIGNATURE.length; i++) {
Expand Down Expand Up @@ -984,4 +1038,3 @@ private void skipWhitespace() {
}
}
}

100 changes: 99 additions & 1 deletion scripts/lib/cn1ss.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,18 @@ cn1ss_setup() {
CN1SS_SOURCE_PATH="$2"
local cache_override="${3:-}" tmp_root

if [ -z "$CN1SS_SOURCE_PATH" ]; then
# Default to common/java if not provided or empty
local script_dir
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CN1SS_SOURCE_PATH="$script_dir/common/java"
fi

if [ -z "$CN1SS_JAVA_BIN" ] || [ ! -x "$CN1SS_JAVA_BIN" ]; then
cn1ss_log "CN1SS setup failed: java binary not executable ($CN1SS_JAVA_BIN)"
return 1
fi
if [ -z "$CN1SS_SOURCE_PATH" ] || [ ! -d "$CN1SS_SOURCE_PATH" ]; then
if [ ! -d "$CN1SS_SOURCE_PATH" ]; then
cn1ss_log "CN1SS setup failed: source directory missing ($CN1SS_SOURCE_PATH)"
return 1
fi
Expand Down Expand Up @@ -305,3 +312,94 @@ cn1ss_post_pr_comment() {
fi
return $rc
}

# Shared function to generate report, compare screenshots, and post PR comment
cn1ss_process_and_report() {
local platform_title="$1"
local compare_json_out="$2"
local summary_out="$3"
local comment_out="$4"
local ref_dir="$5"
local preview_dir="$6"
local artifacts_dir="$7"
# Optional: array of actual entries in format "testName=path"
shift 7
local actual_entries=("$@")

local rc=0

# Run ProcessScreenshots
local -a compare_args=("--reference-dir" "$ref_dir" "--emit-base64" "--preview-dir" "$preview_dir")
for entry in "${actual_entries[@]}"; do
compare_args+=("--actual" "$entry")
done

cn1ss_log "STAGE:COMPARE -> Evaluating screenshots against stored references"
if ! cn1ss_java_run "$CN1SS_PROCESS_CLASS" "${compare_args[@]}" > "$compare_json_out"; then
cn1ss_log "FATAL: Screenshot comparison helper failed"
return 13
fi

# Run RenderScreenshotReport
cn1ss_log "STAGE:COMMENT_BUILD -> Rendering summary and PR comment markdown"
local -a render_args=(
--title "$platform_title"
--compare-json "$compare_json_out"
--comment-out "$comment_out"
--summary-out "$summary_out"
)
if [ -n "${CN1SS_COVERAGE_SUMMARY:-}" ]; then
render_args+=(--coverage-summary "$CN1SS_COVERAGE_SUMMARY")
fi
if [ -n "${CN1SS_COVERAGE_HTML_URL:-}" ]; then
render_args+=(--coverage-html-url "$CN1SS_COVERAGE_HTML_URL")
fi

if ! cn1ss_java_run "$CN1SS_RENDER_CLASS" "${render_args[@]}"; then
cn1ss_log "FATAL: Failed to render screenshot summary/comment"
return 14
fi

if [ -s "$summary_out" ]; then
cn1ss_log " -> Wrote summary entries to $summary_out ($(wc -l < "$summary_out" 2>/dev/null || echo 0) line(s))"
else
cn1ss_log " -> No summary entries generated (all screenshots matched stored baselines)"
fi

if [ -s "$comment_out" ]; then
cn1ss_log " -> Prepared PR comment payload at $comment_out (bytes=$(wc -c < "$comment_out" 2>/dev/null || echo 0))"
else
cn1ss_log " -> No PR comment content produced"
fi

# Process summary entries (copy artifacts, clean up)
if [ -s "$summary_out" ]; then
while IFS='|' read -r status test message copy_flag path preview_note; do
[ -n "${test:-}" ] || continue
cn1ss_log "Test '${test}': ${message}"
if [ "$copy_flag" = "1" ] && [ -n "${path:-}" ] && [ -f "$path" ]; then
cp -f "$path" "$artifacts_dir/${test}.png" 2>/dev/null || true
cn1ss_log " -> Stored PNG artifact copy at $artifacts_dir/${test}.png"
fi
if [ "$status" = "equal" ] && [ -n "${path:-}" ]; then
rm -f "$path" 2>/dev/null || true
fi
if [ -n "${preview_note:-}" ]; then
cn1ss_log " Preview note: ${preview_note}"
fi
done < "$summary_out"
fi

cp -f "$compare_json_out" "$artifacts_dir/screenshot-compare.json" 2>/dev/null || true
if [ -s "$comment_out" ]; then
cp -f "$comment_out" "$artifacts_dir/screenshot-comment.md" 2>/dev/null || true
fi

cn1ss_log "STAGE:COMMENT_POST -> Submitting PR feedback"
local comment_rc=0
if ! cn1ss_post_pr_comment "$comment_out" "$preview_dir"; then
comment_rc=$?
fi

return $comment_rc
}
93 changes: 21 additions & 72 deletions scripts/run-android-instrumentation-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ ra_log() { echo "[run-android-instrumentation-tests] $1"; }
ensure_dir() { mkdir -p "$1" 2>/dev/null || true; }

# CN1SS helpers are implemented in Java for easier maintenance
CN1SS_MAIN_CLASS="Cn1ssChunkTools"
PROCESS_SCREENSHOTS_CLASS="ProcessScreenshots"
RENDER_SCREENSHOT_REPORT_CLASS="RenderScreenshotReport"
# (Defaults for class names are provided by cn1ss.sh)

# ---- Args & environment ----------------------------------------------------

Expand All @@ -25,13 +23,13 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$REPO_ROOT"

CN1SS_HELPER_SOURCE_DIR="$SCRIPT_DIR/android/tests"
CN1SS_HELPER_SOURCE_DIR="$SCRIPT_DIR/common/java"
source "$SCRIPT_DIR/lib/cn1ss.sh"

if [ ! -f "$CN1SS_HELPER_SOURCE_DIR/$CN1SS_MAIN_CLASS.java" ]; then
ra_log "Missing CN1SS helper: $CN1SS_HELPER_SOURCE_DIR/$CN1SS_MAIN_CLASS.java" >&2
exit 3
fi

source "$SCRIPT_DIR/lib/cn1ss.sh"
cn1ss_log() { ra_log "$1"; }

TMPDIR="${TMPDIR:-/tmp}"; TMPDIR="${TMPDIR%/}"
Expand Down Expand Up @@ -220,86 +218,37 @@ done

# ---- Compare against stored references ------------------------------------

COMPARE_ARGS=()
COMPARE_ENTRIES=()
for test in "${TEST_NAMES[@]}"; do
dest="${TEST_OUTPUTS[$test]:-}"
[ -n "$dest" ] || continue
COMPARE_ARGS+=("--actual" "${test}=${dest}")
COMPARE_ENTRIES+=("${test}=${dest}")
done

COMPARE_JSON="$SCREENSHOT_TMP_DIR/screenshot-compare.json"
export CN1SS_PREVIEW_DIR="$SCREENSHOT_PREVIEW_DIR"
ra_log "STAGE:COMPARE -> Evaluating screenshots against stored references"
if ! cn1ss_java_run "$PROCESS_SCREENSHOTS_CLASS" \
--reference-dir "$SCREENSHOT_REF_DIR" \
--emit-base64 \
--preview-dir "$SCREENSHOT_PREVIEW_DIR" \
"${COMPARE_ARGS[@]}" > "$COMPARE_JSON"; then
ra_log "FATAL: Screenshot comparison helper failed"
exit 13
fi

SUMMARY_FILE="$SCREENSHOT_TMP_DIR/screenshot-summary.txt"
COMMENT_FILE="$SCREENSHOT_TMP_DIR/screenshot-comment.md"

ra_log "STAGE:COMMENT_BUILD -> Rendering summary and PR comment markdown"
render_args=(
--compare-json "$COMPARE_JSON"
--comment-out "$COMMENT_FILE"
--summary-out "$SUMMARY_FILE"
--coverage-summary "$COVERAGE_SUMMARY"
)
if [ -n "${ANDROID_COVERAGE_HTML_URL:-}" ]; then
render_args+=(--coverage-html-url "${ANDROID_COVERAGE_HTML_URL}")
fi
if ! cn1ss_java_run "$RENDER_SCREENSHOT_REPORT_CLASS" "${render_args[@]}"; then
ra_log "FATAL: Failed to render screenshot summary/comment"
exit 14
fi

if [ -s "$SUMMARY_FILE" ]; then
ra_log " -> Wrote summary entries to $SUMMARY_FILE ($(wc -l < "$SUMMARY_FILE" 2>/dev/null || echo 0) line(s))"
else
ra_log " -> No summary entries generated (all screenshots matched stored baselines)"
fi

if [ -s "$COMMENT_FILE" ]; then
ra_log " -> Prepared PR comment payload at $COMMENT_FILE (bytes=$(wc -c < "$COMMENT_FILE" 2>/dev/null || echo 0))"
else
ra_log " -> No PR comment content produced"
fi

if [ -s "$SUMMARY_FILE" ]; then
while IFS='|' read -r status test message copy_flag path preview_note; do
[ -n "${test:-}" ] || continue
ra_log "Test '${test}': ${message}"
if [ "$copy_flag" = "1" ] && [ -n "${path:-}" ] && [ -f "$path" ]; then
cp -f "$path" "$ARTIFACTS_DIR/${test}.png" 2>/dev/null || true
ra_log " -> Stored PNG artifact copy at $ARTIFACTS_DIR/${test}.png"
fi
if [ "$status" = "equal" ] && [ -n "${path:-}" ]; then
rm -f "$path" 2>/dev/null || true
fi
if [ -n "${preview_note:-}" ]; then
ra_log " Preview note: ${preview_note}"
fi
done < "$SUMMARY_FILE"
fi

cp -f "$COMPARE_JSON" "$ARTIFACTS_DIR/screenshot-compare.json" 2>/dev/null || true
if [ -s "$COMMENT_FILE" ]; then
cp -f "$COMMENT_FILE" "$ARTIFACTS_DIR/screenshot-comment.md" 2>/dev/null || true
fi

ra_log "STAGE:COMMENT_POST -> Submitting PR feedback"
comment_rc=0
export CN1SS_PREVIEW_DIR="$SCREENSHOT_PREVIEW_DIR"
export CN1SS_COMMENT_MARKER="<!-- CN1SS_ANDROID_COMMENT -->"
export CN1SS_COMMENT_LOG_PREFIX="[run-android-device-tests]"
export CN1SS_PREVIEW_SUBDIR="android"
if ! cn1ss_post_pr_comment "$COMMENT_FILE" "$SCREENSHOT_PREVIEW_DIR"; then
comment_rc=$?
export CN1SS_COVERAGE_SUMMARY="$COVERAGE_SUMMARY"
if [ -n "${ANDROID_COVERAGE_HTML_URL:-}" ]; then
export CN1SS_COVERAGE_HTML_URL="${ANDROID_COVERAGE_HTML_URL}"
fi

cn1ss_process_and_report \
"Android screenshot updates" \
"$COMPARE_JSON" \
"$SUMMARY_FILE" \
"$COMMENT_FILE" \
"$SCREENSHOT_REF_DIR" \
"$SCREENSHOT_PREVIEW_DIR" \
"$ARTIFACTS_DIR" \
"${COMPARE_ENTRIES[@]}"
comment_rc=$?

# Copy useful artifacts for GH Actions
cp -f "$TEST_LOG" "$ARTIFACTS_DIR/device-runner-logcat.txt" 2>/dev/null || true
[ -n "${TEST_EXEC_LOG:-}" ] && cp -f "$TEST_EXEC_LOG" "$ARTIFACTS_DIR/test-results.log" 2>/dev/null || true
Expand Down
Loading
Loading