Skip to content

Commit

Permalink
Added support C++ in the projects (#144)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
polischuks authored Nov 13, 2023
1 parent 0b5d326 commit 10675b3
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 5 deletions.
108 changes: 108 additions & 0 deletions src/main/java/org/hyperskill/hstest/checker/CheckLibraryVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.hyperskill.hstest.checker;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.*;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.time.LocalDate;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Checks if the current version of the library is the latest one on GitHub releases page of the library.
* If not, throws an exception.
*/
public class CheckLibraryVersion {

private String VERSION_FILE = "src/main/java/org/hyperskill/hstest/resources/version.txt";
private String LAST_CHECKED_FILE = "lastCheckedHSTestLibrary.txt";
private String GITHUB_API = "https://api.github.com/repos/hyperskill/hs-test/releases/latest";
private String currentVersion;
private String latestVersion;
public boolean isLatestVersion = true;

public CheckLibraryVersion() {
}

/**
* Checks if the current version of the library is the latest one on GitHub releases page of the library.
* If not, throws an exception.
*/
public void checkVersion() throws IOException {
LocalDate lastChecked = null;
String tempDirectoryPath = System.getProperty("java.io.tmpdir");
File lastCheckedFile = new File(tempDirectoryPath + File.separator + LAST_CHECKED_FILE);
if (lastCheckedFile.exists()) {
try (BufferedReader reader = new BufferedReader(new FileReader(lastCheckedFile))) {
lastChecked = LocalDate.parse(reader.readLine());
}
}
if (LocalDate.now().equals(lastChecked)) {
return;
}
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(VERSION_FILE);
if (inputStream != null) {
currentVersion = new BufferedReader(new InputStreamReader(inputStream)).readLine();
} else return;
latestVersion = getLatestHsTestVersionFromGitHub();
if (!currentVersion.equals(latestVersion)) {
isLatestVersion = false;
}
lastChecked = LocalDate.now();
try (FileWriter writer = new FileWriter(lastCheckedFile)) {
writer.write(lastChecked.toString());
}
}

/**
* Returns latest version of the library from GitHub releases page of the library.
* @return String latest version of the library
*/
private String getLatestHsTestVersionFromGitHub() {
HttpURLConnection connection = null;
int responseCode = -1;
try {
URL url = new URL(GITHUB_API);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/vnd.github+json");
connection.setConnectTimeout(100);
connection.setReadTimeout(500);
responseCode = connection.getResponseCode();
} catch (IOException e) {
return currentVersion;
}
if (responseCode != 200) {
return currentVersion;
}

try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String response = in.lines().collect(Collectors.joining());
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String,Object> map = gson.fromJson(response, type);
return map.get("tag_name").toString().replace("v", "");
} catch (IOException e) {
return currentVersion;
}
}

/**
* Returns feedback for the user if the current version of the library is not the latest one.
* @return String feedback for the user
*/
public String getFeedback() {
return "\nThe installed hs-test version (" + currentVersion + ") is not the latest version (" + latestVersion + "). " +
"Update the library by following the instructions below:\n\n" +
"1. Open your project's dependency file build.gradle.\n" +
"2. Find the hs-test dependency and change its version to the latest one (" + latestVersion + ").\n" +
"3. Sync the dependencies in your development environment or run the following commands in the terminal:\n" +
" For Gradle:\n" +
" gradle clean build --refresh-dependencies\n\n" +
"4. Restart the tests.\n\n";
}
}
1 change: 1 addition & 0 deletions src/main/java/org/hyperskill/hstest/resources/version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
10.0.3
15 changes: 10 additions & 5 deletions src/main/java/org/hyperskill/hstest/stage/StageTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hyperskill.hstest.stage;

import lombok.Getter;
import org.hyperskill.hstest.checker.CheckLibraryVersion;
import org.hyperskill.hstest.common.FileUtils;
import org.hyperskill.hstest.common.ReflectionUtils;
import org.hyperskill.hstest.dynamic.ClassSearcher;
Expand All @@ -14,10 +15,7 @@
import org.hyperskill.hstest.testcase.TestCase;
import org.hyperskill.hstest.testing.TestRun;
import org.hyperskill.hstest.testing.execution.MainMethodExecutor;
import org.hyperskill.hstest.testing.execution.process.GoExecutor;
import org.hyperskill.hstest.testing.execution.process.JavascriptExecutor;
import org.hyperskill.hstest.testing.execution.process.PythonExecutor;
import org.hyperskill.hstest.testing.execution.process.ShellExecutor;
import org.hyperskill.hstest.testing.execution.process.*;
import org.hyperskill.hstest.testing.runner.AsyncDynamicTestingRunner;
import org.hyperskill.hstest.testing.runner.TestRunner;
import org.junit.Test;
Expand Down Expand Up @@ -84,6 +82,9 @@ private TestRunner initRunner() {

for (var folder : walkUserFiles(FileUtils.cwd())) {
for (var file : folder.getFiles()) {
if (file.getName().endsWith(".cpp")) {
return new AsyncDynamicTestingRunner(CppExecutor.class);
}
if (file.getName().endsWith(".go")) {
return new AsyncDynamicTestingRunner(GoExecutor.class);
}
Expand Down Expand Up @@ -162,10 +163,14 @@ public final void start() {

currTestRun = testRun;
CheckResult result = testRun.test();

if (!result.isCorrect()) {
CheckLibraryVersion checkLibraryVersion = new CheckLibraryVersion();
checkLibraryVersion.checkVersion();
String fullFeedback = result.getFeedback() + "\n\n"
+ testRun.getTestCase().getFeedback();
if (!checkLibraryVersion.isLatestVersion) {
fullFeedback += checkLibraryVersion.getFeedback();
}
throw new WrongAnswer(fullFeedback.trim());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.hyperskill.hstest.testing.execution.process;

import org.hyperskill.hstest.testing.execution.ProcessExecutor;
import org.hyperskill.hstest.testing.execution.searcher.CppSearcher;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static org.hyperskill.hstest.common.FileUtils.abspath;
import static org.hyperskill.hstest.common.OsUtils.isWindows;

/**
* Executes C++ runnable files
* (files with main function)
* in the given directory.
*
*/
public class CppExecutor extends ProcessExecutor {
private final String executable;
private final String filename;

public CppExecutor(String sourceName) {
super(new CppSearcher().find(sourceName));

var fileName = runnable.getFile().getName();

var withoutCpp = fileName
.substring(0, fileName.length() - new CppSearcher().extension().length());

if (isWindows()) {
executable = withoutCpp;
filename = executable + ".exe";
} else {
executable = "./" + withoutCpp;
filename = withoutCpp;
}
}

@Override
protected List<String> compilationCommand() {
return List.of("g++", "-std", "c++20", "-pipe", "-O2", "-static", "-o", filename, runnable.getFile().getName());
}

@Override
protected String filterCompilationError(String error) {
// Adapt error filtering if needed
return error;
}

@Override
protected List<String> executionCommand(List<String> args) {
List<String> fullArgs = new ArrayList<>();
fullArgs.add(executable);
fullArgs.addAll(args);

return fullArgs;
}

@Override
protected void cleanup() {
try {
Files.deleteIfExists(Paths.get(abspath(filename)));
} catch (IOException ignored) { }
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.hyperskill.hstest.testing.execution.searcher;

import org.hyperskill.hstest.testing.execution.runnable.RunnableFile;

/**
* Searches for C++ runnable files
* (files with main function)
* in the given directory
* and returns the first one found.
*/
public class CppSearcher extends BaseSearcher {
@Override
public String extension() {
return ".cpp";
}

@Override
public RunnableFile search(String whereToSearch) {
return simpleSearch(whereToSearch,
"int main()",
"(^|\\n)\\s*int\\s+main\\s*\\(.*\\)"
);
}
}

0 comments on commit 10675b3

Please sign in to comment.