Skip to content

Commit 5718d8c

Browse files
committed
bug(#3986): added integration test
1 parent 6cb9341 commit 5718d8c

5 files changed

Lines changed: 181 additions & 61 deletions

File tree

eo-runtime/src/main/java/org/eolang/JavaPath.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
package org.eolang;
66

7+
import java.util.regex.Pattern;
8+
79
/**
810
* Java path.
911
*
@@ -18,6 +20,15 @@
1820
* @since 0.29
1921
*/
2022
final class JavaPath {
23+
/**
24+
* Phi pattern.
25+
*/
26+
private static final Pattern PHI = Pattern.compile("^Φ\\.?");
27+
28+
/**
29+
* Dots pattern.
30+
*/
31+
private static final Pattern DOTS = Pattern.compile("(^|\\.)([^.]+)");
2132

2233
/**
2334
* Object name in eolang notation.
@@ -34,17 +45,8 @@ final class JavaPath {
3445

3546
@Override
3647
public String toString() {
37-
if (!this.object.startsWith("Φ.")) {
38-
throw new IllegalArgumentException(
39-
String.format(
40-
"Can't build path to .java file from FQN not started from '%s.'",
41-
PhPackage.GLOBAL
42-
)
43-
);
44-
}
45-
return this.object
46-
.substring(2)
47-
.replaceAll("(^|\\.)([^.]+)", "$1EO$2")
48+
return DOTS.matcher(JavaPath.PHI.matcher(this.object).replaceAll(""))
49+
.replaceAll("$1EO$2")
4850
.replace("$", "$EO")
4951
.replace("-", "_");
5052
}

eo-runtime/src/main/java/org/eolang/PhPackage.java

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@
55

66
package org.eolang;
77

8-
import java.io.File;
98
import java.lang.reflect.InvocationTargetException;
10-
import java.nio.file.Files;
11-
import java.nio.file.Path;
12-
import java.nio.file.Paths;
139
import java.util.Map;
1410
import java.util.concurrent.ConcurrentHashMap;
1511

@@ -69,18 +65,35 @@ public boolean hasRho() {
6965

7066
@Override
7167
public Phi take(final String name) {
72-
final String obj = String.join(".", this.pkg, name);
73-
final String key = new JavaPath(obj).toString();
74-
return this.objects.computeIfAbsent(
75-
key,
76-
k -> {
77-
final Phi initialized = this.loadPhi(key, obj);
78-
if (!(initialized instanceof PhPackage)) {
79-
initialized.put(Attr.RHO, this);
80-
}
81-
return initialized;
68+
final String fqn = String.join(".", this.pkg, name);
69+
final Phi taken;
70+
if (name.equals(Attr.RHO)) {
71+
if (this.objects.containsKey(Attr.RHO)) {
72+
taken = this.objects.get(Attr.RHO);
73+
} else {
74+
throw new ExUnset(
75+
String.format(
76+
"The %s attribute is absent in package object '%s'",
77+
Attr.RHO, this.pkg
78+
)
79+
);
8280
}
83-
).copy();
81+
} else if (this.objects.containsKey(fqn)) {
82+
taken = this.objects.get(fqn).copy();
83+
} else if (name.contains(".")) {
84+
final String[] parts = name.split("\\.");
85+
Phi next = this.take(parts[0]);
86+
for (int idx = 1; idx < parts.length; ++idx) {
87+
next = next.take(parts[idx]);
88+
}
89+
taken = next;
90+
} else {
91+
final Phi loaded = this.loadPhi(fqn);
92+
loaded.put(Attr.RHO, this);
93+
this.put(fqn, loaded);
94+
taken = this.take(name);
95+
}
96+
return taken;
8497
}
8598

8699
@Override
@@ -99,9 +112,7 @@ public void put(final int pos, final Phi object) {
99112

100113
@Override
101114
public void put(final String name, final Phi object) {
102-
throw new ExFailure(
103-
"Can't #put(%s, %s) to package object \"%s\"", name, object, this.pkg
104-
);
115+
this.objects.put(name, object);
105116
}
106117

107118
@Override
@@ -111,43 +122,41 @@ public byte[] delta() {
111122

112123
/**
113124
* Load phi object by package name from ClassLoader.
114-
* @param path Path to directory or .java file
115-
* @param object Object FQN
116125
* @return Phi
117126
*/
118-
private Phi loadPhi(final String path, final String object) {
119-
final Path pth = new File(
120-
this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath()
121-
).toPath().resolve(path.replace(".", File.separator));
122-
final Phi phi;
123-
if (Files.exists(pth) && Files.isDirectory(pth)) {
124-
phi = new PhPackage(object);
125-
} else {
126-
final Path clazz = Paths.get(String.format("%s.class", pth));
127-
if (!Files.exists(clazz) || Files.isDirectory(clazz)) {
128-
throw new ExFailure(
129-
String.format("Couldn't find object '%s'", object)
130-
);
131-
}
127+
private Phi loadPhi(final String fqn) {
128+
final String target = new JavaPath(fqn).toString();
129+
Phi loaded;
130+
try {
131+
Class.forName(String.format("%s.package-info", target));
132+
loaded = new PhPackage(fqn);
133+
} catch (final ClassNotFoundException pckg) {
132134
try {
133-
phi = (Phi) Class.forName(path)
135+
loaded = (Phi) Class.forName(target)
134136
.getConstructor()
135137
.newInstance();
136-
} catch (final ClassNotFoundException
137-
| NoSuchMethodException
138-
| InvocationTargetException
139-
| InstantiationException
140-
| IllegalAccessException ex
138+
} catch (final ClassNotFoundException phi) {
139+
throw new ExFailure(
140+
String.format(
141+
"Couldn't find object '%s' because there's no class or package '%s'",
142+
fqn, target
143+
),
144+
phi
145+
);
146+
} catch (final NoSuchMethodException
147+
| InvocationTargetException
148+
| InstantiationException
149+
| IllegalAccessException ex
141150
) {
142151
throw new ExFailure(
143152
String.format(
144153
"Couldn't build Java object \"%s\" in EO package \"%s\"",
145-
path, this.pkg
154+
target, this.pkg
146155
),
147156
ex
148157
);
149158
}
150159
}
151-
return phi;
160+
return loaded;
152161
}
153162
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
3+
* SPDX-License-Identifier: MIT
4+
*/
5+
package integration;
6+
7+
import com.jcabi.manifests.Manifests;
8+
import com.yegor256.Jaxec;
9+
import com.yegor256.MayBeSlow;
10+
import com.yegor256.Mktmp;
11+
import com.yegor256.MktmpResolver;
12+
import com.yegor256.WeAreOnline;
13+
import com.yegor256.farea.Farea;
14+
import com.yegor256.farea.RequisiteMatcher;
15+
import java.io.IOException;
16+
import java.nio.charset.StandardCharsets;
17+
import java.nio.file.Path;
18+
import java.nio.file.Paths;
19+
import org.hamcrest.MatcherAssert;
20+
import org.hamcrest.Matchers;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.ExtendWith;
23+
24+
/**
25+
* Integration test that runs simple EO program from packaged jar.
26+
* @since 0.54
27+
*/
28+
@SuppressWarnings({"JTCOP.RuleAllTestsHaveProductionClass", "JTCOP.RuleNotContainsTestWord"})
29+
@ExtendWith(MktmpResolver.class)
30+
final class JarIT {
31+
@Test
32+
@ExtendWith(WeAreOnline.class)
33+
@ExtendWith(MayBeSlow.class)
34+
void runsProgramFromJar(final @Mktmp Path temp) throws IOException {
35+
new Farea(temp).together(
36+
f -> {
37+
f.properties()
38+
.set("project.build.sourceEncoding", StandardCharsets.UTF_8.name())
39+
.set("project.reporting.outputEncoding", StandardCharsets.UTF_8.name());
40+
f.files().file("src/main").save(
41+
Paths.get(System.getProperty("user.dir")).resolve("src/main")
42+
);
43+
f.files()
44+
.file("src/main/eo/simple.eo")
45+
.write(
46+
"QQ.io.stdout \"Hello, world!\" > simple\n".getBytes(StandardCharsets.UTF_8)
47+
);
48+
f.dependencies()
49+
.append(
50+
"org.eolang",
51+
"eo-runtime",
52+
System.getProperty(
53+
"eo.version",
54+
Manifests.read("EO-Version")
55+
)
56+
);
57+
f.build()
58+
.plugins()
59+
.append(
60+
"org.eolang",
61+
"eo-maven-plugin",
62+
System.getProperty(
63+
"eo.version",
64+
Manifests.read("EO-Version")
65+
)
66+
)
67+
.execution("compile")
68+
.phase("generate-sources")
69+
.goals("register", "compile", "transpile")
70+
.configuration()
71+
.set("failOnWarning", Boolean.FALSE.toString())
72+
.set("skipLinting", Boolean.TRUE.toString());
73+
f.exec("clean", "compile", "jar:jar");
74+
MatcherAssert.assertThat(
75+
"Project must be successfully built and packaged into jar",
76+
f.log(),
77+
RequisiteMatcher.SUCCESS
78+
);
79+
MatcherAssert.assertThat(
80+
"Simple program must be successfully executed from jar",
81+
new Jaxec(
82+
"java", "-cp", "test-0.0.0.jar",
83+
"-Dfile.encoding=UTF-8", "-Xss64M", "-Xms64M",
84+
"org.eolang.Main", "simple"
85+
).withHome(temp.resolve("target")).exec().stdout(),
86+
Matchers.allOf(
87+
Matchers.containsString("Hello, world!"),
88+
Matchers.containsString("[0x01] = true")
89+
)
90+
);
91+
}
92+
);
93+
}
94+
}

eo-runtime/src/test/java/integration/SnippetIT.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,6 @@
2828

2929
/**
3030
* Integration test for simple snippets.
31-
*
32-
* <p>This test will/may fail if you change something in {@code to-java.xsl}
33-
* or some other place where Java sources are generated. This happens
34-
* because this test relies on {@code eo-runtime.jar}, which it finds in Maven
35-
* Central. Thus, when changes are made, it is recommended to disable this test.
36-
* Then, when new {@code eo-runtime.jar} is
37-
* released to Maven Central, you enable this test again.</p>
38-
*
3931
* @since 0.1
4032
*/
4133
@SuppressWarnings({"JTCOP.RuleAllTestsHaveProductionClass", "JTCOP.RuleNotContainsTestWord"})

eo-runtime/src/test/java/org/eolang/PhPackageTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,29 @@ void copiesObject() {
4242
);
4343
}
4444

45+
@Test
46+
void doesNotSetRhoToGlobalObject() {
47+
Assertions.assertThrows(
48+
ExUnset.class,
49+
() -> Phi.Φ.take(Attr.RHO),
50+
String.format(
51+
"Global object '%s' must not have %s attribute",
52+
PhPackage.GLOBAL, Attr.RHO
53+
)
54+
);
55+
}
56+
57+
@Test
58+
void setsRhoToPackage() {
59+
final Phi org = Phi.Φ.take("org");
60+
final Phi eolang = org.take("eolang");
61+
MatcherAssert.assertThat(
62+
String.format("The %s attribute must be set to package object on dispatch", Attr.RHO),
63+
eolang.take(Attr.RHO),
64+
Matchers.equalTo(org)
65+
);
66+
}
67+
4568
@Test
4669
void setsRhoToObject() {
4770
final Phi eolang = Phi.Φ.take("org").take("eolang");

0 commit comments

Comments
 (0)