Skip to content

Commit 701c2e6

Browse files
committed
2 parents 41758d4 + 2030faf commit 701c2e6

File tree

5 files changed

+695
-18
lines changed

5 files changed

+695
-18
lines changed
Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,103 @@
11
== Working with Codename One Sources
22

3-
=== Checking out the Sources
3+
The Codename One SDK is published as a Maven multi-module project. Building the
4+
aggregator in the `/maven` directory compiles every module, installs the
5+
artifacts into your local Maven repository, and lets you depend on the exact
6+
snapshot you cloned from GitHub.
7+
8+
=== Prerequisites
9+
10+
Codename One uses multiple JDKs and Maven profiles during the build. Make sure
11+
your development machine includes the following tooling (see
12+
`BUILDING.md` for platform-specific installation tips):
13+
14+
* JDK 8 (required for the core build and Maven invocations)
15+
* JDK 17 (required when compiling the Android port)
16+
* Apache Maven 3.6 or newer
17+
* macOS with Xcode (only if you plan to build or test the iOS port)
18+
19+
=== Preparing the Maven workspace
20+
21+
Clone the repository and run the Maven aggregator from inside the `maven`
22+
subdirectory. This installs the complete SDK (core runtime, simulator, and
23+
ports) into your local Maven cache:
424

525
[source,bash]
626
----
7-
$ mkdir workspace
8-
$ cd workspace
927
$ git clone https://github.com/codenameone/CodenameOne
28+
$ cd CodenameOne/maven
29+
$ mvn install
1030
----
1131

12-
NOTE: Creating a clean "workspace" directory is optional, and there is nothing special about the name "workspace". It is just recommended to create a clean directory into which you check out Codename One, because building Codename One will check out a few dependent projects and place then at the same level as the CodenameOne folder, so having a clean workspace will make it easier to manage.
32+
The default build runs every module and its unit tests. Append `-DskipTests`
33+
if you want to skip the test phases to speed up local builds.
34+
35+
The version printed by Maven will usually end with `-SNAPSHOT` when you build
36+
from the `master` branch. Release builds from Maven Central omit the suffix.
1337

14-
=== Building Sources
38+
=== Installing the Maven archetypes
1539

16-
The Maven aggregator for the SDK lives in the _maven_ directory. Run the following command to build every module and install the artifacts into your local Maven repository:
40+
Codename One ships its project archetypes in a companion repository. Install
41+
them once so that `mvn archetype:generate` (and the Codename One Initializr)
42+
can reference your locally built SDK artifacts:
1743

1844
[source,bash]
1945
----
20-
$ cd CodenameOne/maven
21-
$ mvn -DskipTests install
46+
$ git clone https://github.com/shannah/cn1-maven-archetypes
47+
$ cd cn1-maven-archetypes
48+
$ mvn install
2249
----
2350

24-
TIP: Omit the `-DskipTests` flag if you want the build to execute the full test suite as part of the build.
51+
=== Running tests selectively
2552

26-
=== Running Unit Tests
27-
28-
Codename One's automated tests are expressed as standard Maven modules. You can run the core unit tests with:
53+
The Maven aggregator exposes modules for the different automated test suites.
54+
Run any of these from the `CodenameOne/maven` directory:
2955

56+
* Core unit tests:
57+
+
3058
[source,bash]
3159
----
32-
$ cd CodenameOne/maven
3360
$ mvn -pl core-unittests test
3461
----
3562

36-
The integration tests that drive the simulator and build client live under the `tests` directory. From the same _maven_ directory you can run them with:
37-
63+
* Integration tests (simulator and build client):
64+
+
3865
[source,bash]
3966
----
4067
$ mvn -pl tests -am verify
4168
----
4269

43-
Consult the module READMEs inside `maven/core-unittests` and `maven/tests` for additional configuration options and environment variables.
70+
Refer to the READMEs in `maven/core-unittests` and `maven/tests` for additional
71+
configuration flags, platform requirements, and environment variables.
72+
73+
=== Using your local build in application projects
74+
75+
After running the `mvn install` commands above, the Codename One artifacts are
76+
available in your local Maven repository. To consume that build in an
77+
application generated from the Codename One Initializr (or any other Maven
78+
project), adjust the `pom.xml` properties to reference your snapshot version:
79+
80+
[source,xml]
81+
----
82+
<properties>
83+
<!-- Replace with the snapshot printed during mvn install -->
84+
<cn1.version>7.0.21-SNAPSHOT</cn1.version>
85+
<cn1.plugin.version>7.0.21-SNAPSHOT</cn1.plugin.version>
86+
</properties>
87+
----
88+
89+
Open the project in your IDE and build or run it. Maven will resolve the local
90+
snapshot instead of downloading the latest release from Maven Central.
91+
92+
=== Why build from source?
93+
94+
Building the SDK yourself gives you:
4495

45-
=== Running iOS Unit Tests
96+
* Immediate access to fixes and features before they reach Maven Central.
97+
* The ability to inspect, debug, and modify the framework when you need custom
98+
behavior.
99+
* A path to contribute improvements back to the Codename One core.
46100

47-
Codename One's Appium-based iOS simulator tests require macOS, Xcode, and an Enterprise subscription. If you have access to that infrastructure, coordinate with Codename One support for credentials and provisioning, then follow the instructions in the `maven/tests` module to execute the suite with Maven. The legacy Ant targets documented in older guides are no longer part of the supported workflow.
101+
Once you are comfortable with the baseline build, continue with the scripts in
102+
`scripts/` or the `BUILDING.md` guide to compile specific ports (Android or
103+
iOS) or to automate CI workflows.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package com.codename1.share;
2+
3+
import com.codename1.test.UITestBase;
4+
import com.codename1.ui.Button;
5+
import com.codename1.ui.Command;
6+
import com.codename1.ui.Component;
7+
import com.codename1.ui.Container;
8+
import com.codename1.ui.Form;
9+
import com.codename1.ui.Label;
10+
import com.codename1.ui.TextArea;
11+
import com.codename1.ui.TextField;
12+
import com.codename1.ui.layouts.BorderLayout;
13+
import com.codename1.ui.events.ActionEvent;
14+
import com.codename1.ui.events.ActionListener;
15+
import org.junit.jupiter.api.BeforeEach;
16+
import org.junit.jupiter.api.Test;
17+
18+
import java.lang.reflect.Field;
19+
import java.lang.reflect.Method;
20+
import java.util.List;
21+
import java.util.concurrent.atomic.AtomicInteger;
22+
23+
import static org.junit.jupiter.api.Assertions.*;
24+
import static org.mockito.Mockito.when;
25+
26+
class ShareFormTest extends UITestBase {
27+
28+
@BeforeEach
29+
void setUpImageIO() {
30+
when(implementation.getImageIO()).thenReturn(null);
31+
}
32+
33+
@Test
34+
void testConstructorWithoutRecipientAddsMessageOnly() throws Exception {
35+
AtomicInteger postClicks = new AtomicInteger();
36+
Form contacts = new Form();
37+
ShareForm form = new ShareForm(contacts, "Share", null, "Hello", new RecordingListener(postClicks));
38+
39+
assertEquals("Share", form.getTitle());
40+
assertEquals("Hello", form.getMessage());
41+
assertEquals("", form.getTo());
42+
43+
TextField toField = getField(form, "to", TextField.class);
44+
assertFalse(form.getContentPane().contains(toField));
45+
46+
TextArea messageField = getField(form, "message", TextArea.class);
47+
assertSame(form.getContentPane(), messageField.getParent());
48+
49+
Button postButton = getField(form, "post", Button.class);
50+
triggerButton(postButton);
51+
assertEquals(1, postClicks.get());
52+
53+
assertEquals(BorderLayout.class, form.getContentPane().getLayout().getClass());
54+
}
55+
56+
@Test
57+
void testRecipientFieldIsAddedWhenProvided() throws Exception {
58+
Form contacts = new Form();
59+
ShareForm form = new ShareForm(contacts, "Share", "[email protected]", "Message", new RecordingListener(new AtomicInteger()));
60+
61+
assertEquals("[email protected]", form.getTo());
62+
TextField toField = getField(form, "to", TextField.class);
63+
assertSame(form.getContentPane(), toField.getParent());
64+
}
65+
66+
@Test
67+
void testImageFallbackDisplaysPathTextWhenScalingUnavailable() throws Exception {
68+
Form contacts = new Form();
69+
String imagePath = "missing-image.jpg";
70+
ShareForm form = new ShareForm(contacts, "Share", "[email protected]", "Caption", imagePath, new RecordingListener(new AtomicInteger()));
71+
72+
TextArea messageField = getField(form, "message", TextArea.class);
73+
Container body = (Container) messageField.getParent();
74+
assertNotNull(body);
75+
assertEquals(BorderLayout.class, body.getLayout().getClass());
76+
77+
Label imageLabel = findFirstLabel(body);
78+
assertNotNull(imageLabel);
79+
assertEquals(imagePath, imageLabel.getText());
80+
assertSame(body, imageLabel.getParent());
81+
}
82+
83+
@Test
84+
void testBackCommandInvokesContactsShowBack() {
85+
final AtomicInteger backCalls = new AtomicInteger();
86+
Form contacts = new Form() {
87+
@Override
88+
public void showBack() {
89+
backCalls.incrementAndGet();
90+
}
91+
};
92+
ShareForm form = new ShareForm(contacts, "Share", null, "Body", new RecordingListener(new AtomicInteger()));
93+
94+
Command back = form.getBackCommand();
95+
assertNotNull(back);
96+
back.actionPerformed(null);
97+
assertEquals(1, backCalls.get());
98+
}
99+
100+
private void triggerButton(Button button) throws Exception {
101+
Method fire = Button.class.getDeclaredMethod("fireActionEvent", int.class, int.class);
102+
fire.setAccessible(true);
103+
fire.invoke(button, 0, 0);
104+
}
105+
106+
private Label findFirstLabel(Container root) {
107+
List<Component> children = root.getChildrenAsList(true);
108+
for (Component child : children) {
109+
if (child instanceof Label) {
110+
return (Label) child;
111+
}
112+
if (child instanceof Container) {
113+
Label nested = findFirstLabel((Container) child);
114+
if (nested != null) {
115+
return nested;
116+
}
117+
}
118+
}
119+
return null;
120+
}
121+
122+
private <T> T getField(Object target, String fieldName, Class<T> type) throws Exception {
123+
Field field = target.getClass().getDeclaredField(fieldName);
124+
field.setAccessible(true);
125+
return type.cast(field.get(target));
126+
}
127+
128+
private static class RecordingListener implements ActionListener {
129+
private final AtomicInteger counter;
130+
131+
RecordingListener(AtomicInteger counter) {
132+
this.counter = counter;
133+
}
134+
135+
public void actionPerformed(ActionEvent evt) {
136+
counter.incrementAndGet();
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)