Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support C++ in the projects #144

Merged
merged 9 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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*\\(.*\\)"
);
}
}
Loading