Skip to content

Commit 10675b3

Browse files
authored
Added support C++ in the projects (#144)
* Added CheckLibraryVersion class for check library version * Fixed CheckLibraryVersion class * deleted non-usable dependencies * Fixed exception * Fixed exception * Fixed exception/omit catch block * improvement for CheckLibraryVersion * added cpp executor and cpp searcher (#143) * added cpp executor and cpp searcher * improved regular expression for cpp_searcher.java * Update CppExecutor.java reformat compilation command
1 parent 0b5d326 commit 10675b3

File tree

5 files changed

+213
-5
lines changed

5 files changed

+213
-5
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package org.hyperskill.hstest.checker;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.reflect.TypeToken;
5+
6+
import java.io.*;
7+
import java.lang.reflect.Type;
8+
import java.net.HttpURLConnection;
9+
import java.net.SocketTimeoutException;
10+
import java.net.URL;
11+
import java.time.LocalDate;
12+
import java.util.Map;
13+
import java.util.stream.Collectors;
14+
15+
/**
16+
* Checks if the current version of the library is the latest one on GitHub releases page of the library.
17+
* If not, throws an exception.
18+
*/
19+
public class CheckLibraryVersion {
20+
21+
private String VERSION_FILE = "src/main/java/org/hyperskill/hstest/resources/version.txt";
22+
private String LAST_CHECKED_FILE = "lastCheckedHSTestLibrary.txt";
23+
private String GITHUB_API = "https://api.github.com/repos/hyperskill/hs-test/releases/latest";
24+
private String currentVersion;
25+
private String latestVersion;
26+
public boolean isLatestVersion = true;
27+
28+
public CheckLibraryVersion() {
29+
}
30+
31+
/**
32+
* Checks if the current version of the library is the latest one on GitHub releases page of the library.
33+
* If not, throws an exception.
34+
*/
35+
public void checkVersion() throws IOException {
36+
LocalDate lastChecked = null;
37+
String tempDirectoryPath = System.getProperty("java.io.tmpdir");
38+
File lastCheckedFile = new File(tempDirectoryPath + File.separator + LAST_CHECKED_FILE);
39+
if (lastCheckedFile.exists()) {
40+
try (BufferedReader reader = new BufferedReader(new FileReader(lastCheckedFile))) {
41+
lastChecked = LocalDate.parse(reader.readLine());
42+
}
43+
}
44+
if (LocalDate.now().equals(lastChecked)) {
45+
return;
46+
}
47+
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(VERSION_FILE);
48+
if (inputStream != null) {
49+
currentVersion = new BufferedReader(new InputStreamReader(inputStream)).readLine();
50+
} else return;
51+
latestVersion = getLatestHsTestVersionFromGitHub();
52+
if (!currentVersion.equals(latestVersion)) {
53+
isLatestVersion = false;
54+
}
55+
lastChecked = LocalDate.now();
56+
try (FileWriter writer = new FileWriter(lastCheckedFile)) {
57+
writer.write(lastChecked.toString());
58+
}
59+
}
60+
61+
/**
62+
* Returns latest version of the library from GitHub releases page of the library.
63+
* @return String latest version of the library
64+
*/
65+
private String getLatestHsTestVersionFromGitHub() {
66+
HttpURLConnection connection = null;
67+
int responseCode = -1;
68+
try {
69+
URL url = new URL(GITHUB_API);
70+
connection = (HttpURLConnection) url.openConnection();
71+
connection.setRequestMethod("GET");
72+
connection.setRequestProperty("Accept", "application/vnd.github+json");
73+
connection.setConnectTimeout(100);
74+
connection.setReadTimeout(500);
75+
responseCode = connection.getResponseCode();
76+
} catch (IOException e) {
77+
return currentVersion;
78+
}
79+
if (responseCode != 200) {
80+
return currentVersion;
81+
}
82+
83+
try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
84+
String response = in.lines().collect(Collectors.joining());
85+
Gson gson = new Gson();
86+
Type type = new TypeToken<Map<String, Object>>(){}.getType();
87+
Map<String,Object> map = gson.fromJson(response, type);
88+
return map.get("tag_name").toString().replace("v", "");
89+
} catch (IOException e) {
90+
return currentVersion;
91+
}
92+
}
93+
94+
/**
95+
* Returns feedback for the user if the current version of the library is not the latest one.
96+
* @return String feedback for the user
97+
*/
98+
public String getFeedback() {
99+
return "\nThe installed hs-test version (" + currentVersion + ") is not the latest version (" + latestVersion + "). " +
100+
"Update the library by following the instructions below:\n\n" +
101+
"1. Open your project's dependency file build.gradle.\n" +
102+
"2. Find the hs-test dependency and change its version to the latest one (" + latestVersion + ").\n" +
103+
"3. Sync the dependencies in your development environment or run the following commands in the terminal:\n" +
104+
" For Gradle:\n" +
105+
" gradle clean build --refresh-dependencies\n\n" +
106+
"4. Restart the tests.\n\n";
107+
}
108+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10.0.3

src/main/java/org/hyperskill/hstest/stage/StageTest.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.hyperskill.hstest.stage;
22

33
import lombok.Getter;
4+
import org.hyperskill.hstest.checker.CheckLibraryVersion;
45
import org.hyperskill.hstest.common.FileUtils;
56
import org.hyperskill.hstest.common.ReflectionUtils;
67
import org.hyperskill.hstest.dynamic.ClassSearcher;
@@ -14,10 +15,7 @@
1415
import org.hyperskill.hstest.testcase.TestCase;
1516
import org.hyperskill.hstest.testing.TestRun;
1617
import org.hyperskill.hstest.testing.execution.MainMethodExecutor;
17-
import org.hyperskill.hstest.testing.execution.process.GoExecutor;
18-
import org.hyperskill.hstest.testing.execution.process.JavascriptExecutor;
19-
import org.hyperskill.hstest.testing.execution.process.PythonExecutor;
20-
import org.hyperskill.hstest.testing.execution.process.ShellExecutor;
18+
import org.hyperskill.hstest.testing.execution.process.*;
2119
import org.hyperskill.hstest.testing.runner.AsyncDynamicTestingRunner;
2220
import org.hyperskill.hstest.testing.runner.TestRunner;
2321
import org.junit.Test;
@@ -84,6 +82,9 @@ private TestRunner initRunner() {
8482

8583
for (var folder : walkUserFiles(FileUtils.cwd())) {
8684
for (var file : folder.getFiles()) {
85+
if (file.getName().endsWith(".cpp")) {
86+
return new AsyncDynamicTestingRunner(CppExecutor.class);
87+
}
8788
if (file.getName().endsWith(".go")) {
8889
return new AsyncDynamicTestingRunner(GoExecutor.class);
8990
}
@@ -162,10 +163,14 @@ public final void start() {
162163

163164
currTestRun = testRun;
164165
CheckResult result = testRun.test();
165-
166166
if (!result.isCorrect()) {
167+
CheckLibraryVersion checkLibraryVersion = new CheckLibraryVersion();
168+
checkLibraryVersion.checkVersion();
167169
String fullFeedback = result.getFeedback() + "\n\n"
168170
+ testRun.getTestCase().getFeedback();
171+
if (!checkLibraryVersion.isLatestVersion) {
172+
fullFeedback += checkLibraryVersion.getFeedback();
173+
}
169174
throw new WrongAnswer(fullFeedback.trim());
170175
}
171176

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.hyperskill.hstest.testing.execution.process;
2+
3+
import org.hyperskill.hstest.testing.execution.ProcessExecutor;
4+
import org.hyperskill.hstest.testing.execution.searcher.CppSearcher;
5+
6+
import java.io.IOException;
7+
import java.nio.file.Files;
8+
import java.nio.file.Paths;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.stream.Collectors;
12+
13+
import static org.hyperskill.hstest.common.FileUtils.abspath;
14+
import static org.hyperskill.hstest.common.OsUtils.isWindows;
15+
16+
/**
17+
* Executes C++ runnable files
18+
* (files with main function)
19+
* in the given directory.
20+
*
21+
*/
22+
public class CppExecutor extends ProcessExecutor {
23+
private final String executable;
24+
private final String filename;
25+
26+
public CppExecutor(String sourceName) {
27+
super(new CppSearcher().find(sourceName));
28+
29+
var fileName = runnable.getFile().getName();
30+
31+
var withoutCpp = fileName
32+
.substring(0, fileName.length() - new CppSearcher().extension().length());
33+
34+
if (isWindows()) {
35+
executable = withoutCpp;
36+
filename = executable + ".exe";
37+
} else {
38+
executable = "./" + withoutCpp;
39+
filename = withoutCpp;
40+
}
41+
}
42+
43+
@Override
44+
protected List<String> compilationCommand() {
45+
return List.of("g++", "-std", "c++20", "-pipe", "-O2", "-static", "-o", filename, runnable.getFile().getName());
46+
}
47+
48+
@Override
49+
protected String filterCompilationError(String error) {
50+
// Adapt error filtering if needed
51+
return error;
52+
}
53+
54+
@Override
55+
protected List<String> executionCommand(List<String> args) {
56+
List<String> fullArgs = new ArrayList<>();
57+
fullArgs.add(executable);
58+
fullArgs.addAll(args);
59+
60+
return fullArgs;
61+
}
62+
63+
@Override
64+
protected void cleanup() {
65+
try {
66+
Files.deleteIfExists(Paths.get(abspath(filename)));
67+
} catch (IOException ignored) { }
68+
}
69+
}
70+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.hyperskill.hstest.testing.execution.searcher;
2+
3+
import org.hyperskill.hstest.testing.execution.runnable.RunnableFile;
4+
5+
/**
6+
* Searches for C++ runnable files
7+
* (files with main function)
8+
* in the given directory
9+
* and returns the first one found.
10+
*/
11+
public class CppSearcher extends BaseSearcher {
12+
@Override
13+
public String extension() {
14+
return ".cpp";
15+
}
16+
17+
@Override
18+
public RunnableFile search(String whereToSearch) {
19+
return simpleSearch(whereToSearch,
20+
"int main()",
21+
"(^|\\n)\\s*int\\s+main\\s*\\(.*\\)"
22+
);
23+
}
24+
}

0 commit comments

Comments
 (0)