Skip to content

Commit 135946c

Browse files
authored
Wire screenshot comparison into developer guide workflow (#4072)
* Run animation screenshot comparison in docs workflow * Fix animation demo imports for CI build * Revert LazyValue documentation tags * Fix animation doc includes and lazy value snippet * Adjust animation guide includes and inline LazyValue snippet * Set animation guide includes to indent zero
1 parent 38e31bc commit 135946c

23 files changed

+1100
-497
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
6+
BASELINE_DIR="${BASELINE_DIR:-${PROJECT_ROOT}/docs/developer-guide/img}"
7+
STORAGE_DIR="${CN1_STORAGE_DIR:-${HOME}/.cn1}"
8+
ARTIFACT_DIR="${1:-${PROJECT_ROOT}/docs/demos/animation-screenshot-artifacts}"
9+
10+
if ! command -v compare >/dev/null 2>&1; then
11+
echo "ImageMagick 'compare' command is required." >&2
12+
exit 2
13+
fi
14+
15+
mkdir -p "${ARTIFACT_DIR}"
16+
find "${ARTIFACT_DIR}" -mindepth 1 -delete
17+
18+
if [[ ! -d "${STORAGE_DIR}" ]]; then
19+
echo "Storage directory ${STORAGE_DIR} does not exist; nothing to compare."
20+
exit 0
21+
fi
22+
23+
shopt -s nullglob
24+
mapfile -d '' SCREENSHOTS < <(find "${STORAGE_DIR}" -type f -name '*.png' -print0)
25+
shopt -u nullglob
26+
27+
if [[ ${#SCREENSHOTS[@]} -eq 0 ]]; then
28+
echo "No screenshots found under ${STORAGE_DIR}; nothing to compare."
29+
exit 0
30+
fi
31+
32+
mismatch_count=0
33+
34+
for screenshot in "${SCREENSHOTS[@]}"; do
35+
filename="$(basename "${screenshot}")"
36+
base="${filename%.png}"
37+
38+
baseline=""
39+
baseline_ext=""
40+
for ext in png PNG jpg JPG jpeg JPEG; do
41+
candidate="${BASELINE_DIR}/${base}.${ext}"
42+
if [[ -f "${candidate}" ]]; then
43+
baseline="${candidate}"
44+
baseline_ext="${ext}"
45+
break
46+
fi
47+
done
48+
49+
if [[ -z "${baseline}" ]]; then
50+
echo "No baseline found for ${filename}; treating as mismatch." >&2
51+
mismatch_count=$((mismatch_count + 1))
52+
cp "${screenshot}" "${ARTIFACT_DIR}/${filename}"
53+
rm -f "${screenshot}"
54+
continue
55+
fi
56+
57+
metric_file="$(mktemp)"
58+
diff_image="${ARTIFACT_DIR}/${base}.diff.png"
59+
60+
set +e
61+
compare -metric AE "${screenshot}" "${baseline}" "${diff_image}" 2>"${metric_file}"
62+
status=$?
63+
set -e
64+
65+
if [[ ${status} -eq 0 ]]; then
66+
rm -f "${screenshot}" "${diff_image}" "${metric_file}"
67+
echo "${filename} matches baseline; capture discarded."
68+
continue
69+
fi
70+
71+
if [[ ${status} -ne 1 ]]; then
72+
cat "${metric_file}" >&2
73+
rm -f "${metric_file}" "${diff_image}"
74+
echo "ImageMagick comparison failed for ${filename}." >&2
75+
exit ${status}
76+
fi
77+
78+
mismatch_count=$((mismatch_count + 1))
79+
metric_value="$(cat "${metric_file}")"
80+
rm -f "${metric_file}"
81+
82+
cp "${screenshot}" "${ARTIFACT_DIR}/${filename}"
83+
cp "${baseline}" "${ARTIFACT_DIR}/${base}.baseline.${baseline_ext}"
84+
echo "${metric_value}" > "${ARTIFACT_DIR}/${base}.metric.txt"
85+
rm -f "${screenshot}"
86+
echo "${filename} differs from baseline; artifacts stored in ${ARTIFACT_DIR}." >&2
87+
done
88+
89+
if [[ ${mismatch_count} -eq 0 ]]; then
90+
echo "All screenshots match their baselines."
91+
exit 0
92+
fi
93+
94+
echo "${mismatch_count} screenshot(s) differ from the documented baseline." >&2
95+
exit 1

.github/workflows/developer-guide-docs.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,37 @@ jobs:
4949
cp maven/CodeNameOneBuildClient.jar "$HOME/.codenameone/CodeNameOneBuildClient.jar"
5050
xvfb-run -a mvn -B -ntp -Dgenerate-gui-sources-done=true -pl common -am -f docs/demos/pom.xml test
5151
52+
- name: Install ImageMagick for screenshot comparison
53+
if: github.event_name != 'pull_request' || steps.changes.outputs.demos == 'true'
54+
run: |
55+
set -euo pipefail
56+
sudo apt-get update
57+
sudo apt-get install -y --no-install-recommends imagemagick
58+
59+
- name: Compare animation screenshots
60+
id: compare_animation_screenshots
61+
if: github.event_name != 'pull_request' || steps.changes.outputs.demos == 'true'
62+
continue-on-error: true
63+
run: |
64+
set -euo pipefail
65+
ARTIFACT_DIR="build/developer-guide/animation-screenshots"
66+
mkdir -p "${ARTIFACT_DIR}"
67+
.github/scripts/compare-animation-screenshots.sh "${ARTIFACT_DIR}"
68+
69+
- name: Upload animation screenshot mismatches
70+
if: steps.compare_animation_screenshots.outcome == 'failure'
71+
uses: actions/upload-artifact@v4
72+
with:
73+
name: developer-guide-animation-screenshots
74+
path: build/developer-guide/animation-screenshots
75+
if-no-files-found: warn
76+
77+
- name: Fail when animation screenshots differ
78+
if: steps.compare_animation_screenshots.outcome == 'failure'
79+
run: |
80+
echo "Animation demo screenshots differ from developer guide assets." >&2
81+
exit 1
82+
5283
- name: Determine publication metadata
5384
run: |
5485
set -euo pipefail

docs/demos/common/src/main/java/com/codenameone/developerguide/CounterDemo.java

Lines changed: 0 additions & 61 deletions
This file was deleted.

docs/demos/common/src/main/java/com/codenameone/developerguide/DemoRegistry.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
package com.codenameone.developerguide;
22

3+
import com.codenameone.developerguide.animations.AnimationManagerDemo;
4+
import com.codenameone.developerguide.animations.AnimationSynchronicityDemo;
5+
import com.codenameone.developerguide.animations.BubbleTransitionDemo;
6+
import com.codenameone.developerguide.animations.HiddenComponentDemo;
7+
import com.codenameone.developerguide.animations.HierarchyAnimationDemo;
8+
import com.codenameone.developerguide.animations.LayoutAnimationsDemo;
9+
import com.codenameone.developerguide.animations.LowLevelAnimationDemo;
10+
import com.codenameone.developerguide.animations.MorphTransitionDemo;
11+
import com.codenameone.developerguide.animations.ReplaceTransitionDemo;
12+
import com.codenameone.developerguide.animations.SlideTransitionsDemo;
13+
import com.codenameone.developerguide.animations.SwipeBackSupportDemo;
14+
import com.codenameone.developerguide.animations.UnlayoutAnimationsDemo;
15+
316
import java.util.Arrays;
417
import java.util.Collections;
518
import java.util.List;
@@ -10,8 +23,18 @@
1023
public final class DemoRegistry {
1124
private static final List<Demo> DEMOS = Collections.unmodifiableList(
1225
Arrays.asList(
13-
new HelloWorldDemo(),
14-
new CounterDemo()
26+
new LayoutAnimationsDemo(),
27+
new UnlayoutAnimationsDemo(),
28+
new HiddenComponentDemo(),
29+
new AnimationSynchronicityDemo(),
30+
new HierarchyAnimationDemo(),
31+
new AnimationManagerDemo(),
32+
new LowLevelAnimationDemo(),
33+
new ReplaceTransitionDemo(),
34+
new SlideTransitionsDemo(),
35+
new BubbleTransitionDemo(),
36+
new MorphTransitionDemo(),
37+
new SwipeBackSupportDemo()
1538
)
1639
);
1740

docs/demos/common/src/main/java/com/codenameone/developerguide/HelloWorldDemo.java

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.codenameone.developerguide.animations;
2+
3+
import com.codename1.ui.Form;
4+
import com.codename1.ui.Toolbar;
5+
6+
/**
7+
* Utility helpers shared by the animation demos.
8+
*/
9+
final class AnimationDemoUtil {
10+
private AnimationDemoUtil() {
11+
}
12+
13+
static void show(Form form, Form parent) {
14+
if (parent != null) {
15+
Toolbar toolbar = form.getToolbar();
16+
toolbar.setBackCommand(toolbar.addCommandToLeftBar("Back", null, e -> parent.showBack()));
17+
}
18+
form.show();
19+
}
20+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.codenameone.developerguide.animations;
2+
3+
import com.codename1.ui.Button;
4+
import com.codename1.ui.Container;
5+
import com.codename1.ui.Form;
6+
import com.codename1.ui.layouts.BoxLayout;
7+
import com.codenameone.developerguide.Demo;
8+
9+
/**
10+
* Demonstrates interaction with the AnimationManager.
11+
*/
12+
public class AnimationManagerDemo implements Demo {
13+
@Override
14+
public String getTitle() {
15+
return "Animation Manager";
16+
}
17+
18+
@Override
19+
public String getDescription() {
20+
return "Shows how AnimationManager queues operations.";
21+
}
22+
23+
@Override
24+
public void show(Form parent) {
25+
Form hi = new Form("Animation Manager", BoxLayout.y());
26+
Container cnt = new Container(BoxLayout.y());
27+
Button myButton = new Button("Animated");
28+
cnt.add(myButton);
29+
hi.add(cnt);
30+
AnimationDemoUtil.show(hi, parent);
31+
// Methods below exist only so their code can be included in the guide.
32+
}
33+
34+
private void demoProblem(Container cnt, Button myButton) {
35+
// tag::animationManagerProblem[]
36+
cnt.add(myButton);
37+
int componentCount = cnt.getComponentCount();
38+
cnt.animateLayout(300);
39+
cnt.removeComponent(myButton);
40+
if(componentCount == cnt.getComponentCount()) {
41+
// this will happen...
42+
}
43+
// end::animationManagerProblem[]
44+
}
45+
46+
private void demoWait(Container cnt, Button myButton) {
47+
// tag::animationManagerWait[]
48+
cnt.add(myButton);
49+
int componentCount = cnt.getComponentCount();
50+
cnt.animateLayoutAndWait(300);
51+
cnt.removeComponent(myButton);
52+
if(componentCount == cnt.getComponentCount()) {
53+
// this probably won't happen...
54+
}
55+
// end::animationManagerWait[]
56+
}
57+
58+
private void demoFlush(Container cnt, Button myButton) {
59+
// tag::animationManagerFlush[]
60+
cnt.add(myButton);
61+
int componentCount = cnt.getComponentCount();
62+
cnt.animateLayout(300);
63+
cnt.getAnimationManager().flushAnimation(() -> {
64+
cnt.removeComponent(myButton);
65+
if(componentCount == cnt.getComponentCount()) {
66+
// this shouldn't happen...
67+
}
68+
});
69+
// end::animationManagerFlush[]
70+
}
71+
}

0 commit comments

Comments
 (0)