Initial commit
This commit is contained in:
39 files changed
+10132
No files matched your search
@@ -0,0 +1,3 @@
|
||||
.cache
|
||||
build
|
||||
tests/build
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"cmake.generator": "Unix Makefiles"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(substagent VERSION 0.1 LANGUAGES C)
|
||||
|
||||
set(BUILD_INFO_IN "${CMAKE_CURRENT_SOURCE_DIR}/src/build_info.h.in")
|
||||
set(BUILD_INFO_OUT "${CMAKE_CURRENT_BINARY_DIR}/build_info.h")
|
||||
|
||||
add_custom_target(build_info ALL
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DSRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
-P "${CMAKE_SOURCE_DIR}/cmake/gen_build_info.cmake"
|
||||
BYPRODUCTS "${BUILD_INFO_OUT}"
|
||||
COMMENT "Generating build_info.h with current git hash"
|
||||
)
|
||||
|
||||
find_package(JNI REQUIRED)
|
||||
|
||||
add_library(substagent SHARED src/substagent.c)
|
||||
add_library(stb INTERFACE)
|
||||
|
||||
target_include_directories(stb INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/external/stb)
|
||||
target_include_directories(substagent PRIVATE ${JNI_INCLUDE_DIRS})
|
||||
target_include_directories(substagent PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_link_libraries(substagent PRIVATE stb)
|
||||
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <https://unlicense.org>
|
||||
@@ -0,0 +1,10 @@
|
||||
# SubstAgent-JVMTI
|
||||
A Java Agent written in C that listens for strings containing environment variables and substitutes them accordingly. It is intended for use with Spigot plugins to enable environment variable support in their configuration files.
|
||||
|
||||
# How to use
|
||||
`java -agentpath:/full/path/to/libsubstagent.so [rest of your command as normal]`\
|
||||
For example:\
|
||||
`java -agentpath:/full/path/to/libsubstagent.so -jar server.jar nogui`
|
||||
|
||||
# Tests
|
||||
The test script assumes that the generator is set to make (`-G "Unix Makefiles"`)
|
||||
@@ -0,0 +1,17 @@
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short=10 HEAD
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
RESULT_VARIABLE GIT_RESULT
|
||||
)
|
||||
|
||||
if(NOT GIT_RESULT EQUAL 0)
|
||||
set(GIT_HASH "unknown")
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
${SRC_DIR}/src/build_info.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/build_info.h
|
||||
@ONLY
|
||||
)
|
||||
Vendored
+1895
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define GIT_HASH "@GIT_HASH@"
|
||||
@@ -0,0 +1,353 @@
|
||||
#include <errno.h>
|
||||
#include <jvmti.h>
|
||||
#include <jni.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#define STB_DS_IMPLEMENTATION
|
||||
#include "stb_ds.h"
|
||||
#include "build_info.h"
|
||||
|
||||
static jvmtiEnv* jvmti = NULL;
|
||||
|
||||
typedef jint (JNICALL *open0_t)(JNIEnv*, jobject, jstring);
|
||||
typedef jint (JNICALL *readBytes_t)(JNIEnv*, jobject, jbyteArray, jint, jint);
|
||||
static volatile readBytes_t real_readBytes = NULL;
|
||||
static volatile open0_t real_open0 = NULL;
|
||||
|
||||
struct file_data{
|
||||
signed char* data;
|
||||
size_t data_len;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
struct file_entry{
|
||||
jint key;
|
||||
struct file_data value;
|
||||
} *files_map = NULL;
|
||||
|
||||
// Check return value for .data == NULL
|
||||
struct file_data make_file_data(const signed char* data, size_t len, size_t offset) {
|
||||
struct file_data fd;
|
||||
|
||||
fd.data = malloc(len);
|
||||
if (fd.data == NULL) {
|
||||
fd.data_len = 0;
|
||||
return fd;
|
||||
}
|
||||
|
||||
memcpy(fd.data, data, len);
|
||||
fd.data_len = len;
|
||||
fd.offset = offset;
|
||||
return fd;
|
||||
}
|
||||
|
||||
static const jlong TAG = 1;
|
||||
|
||||
static void tag(jobject obj) {
|
||||
if (obj && jvmti) {
|
||||
(*jvmti)->SetTag(jvmti, obj, TAG);
|
||||
}
|
||||
}
|
||||
|
||||
static int has_tag(jobject obj) {
|
||||
jlong tag = 0;
|
||||
if (obj && jvmti) {
|
||||
(*jvmti)->GetTag(jvmti, obj, &tag);
|
||||
}
|
||||
return tag == 1;
|
||||
}
|
||||
|
||||
static int is_config_path(const char* path) {
|
||||
char* extString = strrchr(path, '.');
|
||||
if (extString) {
|
||||
return strcmp(extString, ".yml") == 0 || strcmp(extString, ".yaml") == 0 || strcmp(extString, ".json") == 0 || strcmp(extString, ".txt") == 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) < (b) ? (b) : (a))
|
||||
|
||||
static void substitute(const char* value, size_t value_len, int dollar_sign_matched_index, size_t i, ssize_t len_diff, jsize ba_length, signed char* arr, jsize* used_bytes, signed char* exceeded, size_t* exceeded_len) {
|
||||
signed char before[dollar_sign_matched_index];
|
||||
size_t after_len = MAX(ba_length - (ssize_t)i - len_diff, 0);
|
||||
signed char after[after_len];
|
||||
memcpy(before, arr, dollar_sign_matched_index);
|
||||
memcpy(after, arr+i, after_len);
|
||||
|
||||
signed char result[ba_length];
|
||||
memcpy(result, before, dollar_sign_matched_index);
|
||||
memcpy(result + dollar_sign_matched_index, value, value_len);
|
||||
memcpy(result + dollar_sign_matched_index + value_len, after, after_len);
|
||||
|
||||
|
||||
if (len_diff < 0) {
|
||||
// len_diff is negative so +- which equates to -
|
||||
*used_bytes += len_diff;
|
||||
} else if (len_diff > 0) {
|
||||
ssize_t clamped_free_bytes = MAX(ba_length - *used_bytes, 0);
|
||||
size_t already_copied_bytes = 0;
|
||||
if (clamped_free_bytes > 0) {
|
||||
already_copied_bytes = MIN(len_diff, clamped_free_bytes);
|
||||
*used_bytes += already_copied_bytes;
|
||||
}
|
||||
size_t new_exceeded_len = *exceeded_len + len_diff - already_copied_bytes;
|
||||
exceeded = realloc(exceeded, new_exceeded_len);
|
||||
memcpy(exceeded, exceeded, *exceeded_len);
|
||||
memcpy(exceeded+*exceeded_len, arr + (ba_length - len_diff) + already_copied_bytes, len_diff - already_copied_bytes);
|
||||
*exceeded_len = new_exceeded_len;
|
||||
}
|
||||
|
||||
memcpy(arr, result, ba_length);
|
||||
}
|
||||
|
||||
static jint JNICALL readBytes_hook(JNIEnv* env, jobject thiz, jbyteArray buf, jint off, jint len) {
|
||||
jclass class = (*env)->GetObjectClass(env, thiz);
|
||||
jmethodID hash_code_method = (*env)->GetMethodID(env, class, "hashCode", "()I");
|
||||
|
||||
jint hash = (*env)->CallIntMethod(env, thiz, hash_code_method);
|
||||
struct file_entry *fe = hmgetp(files_map, hash);
|
||||
|
||||
jint r = real_readBytes(env, thiz, buf, off, len);
|
||||
if (!has_tag(thiz)) {
|
||||
return r;
|
||||
}
|
||||
|
||||
jmethodID length_method_id = (*env)->GetMethodID(env, class, "length", "()J");
|
||||
jlong length = (*env)->CallLongMethod(env, thiz, length_method_id);
|
||||
|
||||
jmethodID position_method_id = (*env)->GetMethodID(env, class, "position", "()J");
|
||||
jlong position = (*env)->CallLongMethod(env, thiz, position_method_id);
|
||||
|
||||
int last_data = length - position == 0;
|
||||
|
||||
jsize ba_length = (*env)->GetArrayLength(env, buf);
|
||||
jbyte *ba = (*env)->GetByteArrayElements(env, buf, JNI_FALSE);
|
||||
|
||||
if (r == -1) {
|
||||
if (fe && fe->key != hash) {
|
||||
(*env)->ReleaseByteArrayElements(env, buf, ba, 0);
|
||||
return -1;
|
||||
} else {
|
||||
struct file_data fd = fe->value;
|
||||
size_t writeDataLen = MIN(fd.data_len, ba_length);
|
||||
memcpy(ba, fd.data, writeDataLen);
|
||||
if (ba_length < fd.data_len) {
|
||||
signed char data[fd.data_len - ba_length];
|
||||
memcpy(data, fd.data+ba_length, fd.data_len - ba_length);
|
||||
struct file_data new_fd = make_file_data(data, fd.data_len-ba_length, 0);
|
||||
if (new_fd.data == NULL) {
|
||||
fprintf(stderr, "\e[0;31m[SubstAgent] Malloc failed\e[0m\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
hmput(files_map, hash, fd);
|
||||
}
|
||||
(*env)->ReleaseByteArrayElements(env, buf, ba, 0);
|
||||
return writeDataLen;
|
||||
}
|
||||
}
|
||||
|
||||
jsize used_bytes = r;
|
||||
if (ba) {
|
||||
signed char* arr = malloc(ba_length);
|
||||
signed char* exceeded = malloc(0);
|
||||
size_t exceeded_len = 0;
|
||||
|
||||
if (fe->key == hash) {
|
||||
struct file_data fd = fe->value;
|
||||
ssize_t orig_fitting_data_len = ba_length - fd.data_len;
|
||||
if (orig_fitting_data_len > 0) {
|
||||
memcpy(arr+fd.data_len, ba, orig_fitting_data_len);
|
||||
}
|
||||
size_t dataLen = MIN(fd.data_len, ba_length);
|
||||
ssize_t offset = 0;
|
||||
if (off != 0) {
|
||||
offset = fd.offset - off;
|
||||
}
|
||||
for (ssize_t i = offset; i < dataLen; i++) {
|
||||
if (i < 0) continue;
|
||||
arr[i] = fd.data[i];
|
||||
}
|
||||
|
||||
ssize_t free_bytes = ba_length - used_bytes;
|
||||
size_t already_copied_bytes = 0;
|
||||
if (free_bytes > 0) {
|
||||
already_copied_bytes = MIN(fd.data_len, free_bytes);
|
||||
used_bytes += already_copied_bytes;
|
||||
}
|
||||
|
||||
size_t left_over_exceeded = MAX((ssize_t)fd.data_len - (ssize_t)ba_length, 0);
|
||||
exceeded = realloc(exceeded, left_over_exceeded + fd.data_len - left_over_exceeded);
|
||||
memcpy(exceeded, fd.data+fd.data_len-left_over_exceeded, left_over_exceeded);
|
||||
memcpy(exceeded+left_over_exceeded, ba+(ba_length-fd.data_len)+left_over_exceeded, fd.data_len-left_over_exceeded);
|
||||
exceeded_len = left_over_exceeded + fd.data_len - left_over_exceeded;
|
||||
} else {
|
||||
memcpy(arr, ba, ba_length);
|
||||
}
|
||||
|
||||
int dollar_sign_matched_index = -1;
|
||||
char env_var[4096];
|
||||
env_var[0] = '\0';
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
unsigned char c = arr[i];
|
||||
if (dollar_sign_matched_index != -1) {
|
||||
if ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c == 95) {
|
||||
size_t len = strlen(env_var);
|
||||
env_var[len] = c;
|
||||
env_var[len + 1] = '\0';
|
||||
} else {
|
||||
if (strlen(env_var) == 0) continue;
|
||||
char* value = getenv(env_var);
|
||||
if (!value) {
|
||||
dollar_sign_matched_index = -1;
|
||||
env_var[0] = '\0';
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t env_len = i - dollar_sign_matched_index;
|
||||
size_t value_len = strlen(value);
|
||||
ssize_t len_diff = value_len - env_len;
|
||||
|
||||
substitute(value, value_len, dollar_sign_matched_index, i, len_diff, ba_length, arr, &used_bytes, exceeded, &exceeded_len);
|
||||
|
||||
dollar_sign_matched_index = -1;
|
||||
env_var[0] = '\0';
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (c == '$') {
|
||||
dollar_sign_matched_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_data && dollar_sign_matched_index != -1) {
|
||||
if (strlen(env_var) != 0) {
|
||||
char* value = getenv(env_var);
|
||||
if (value) {
|
||||
size_t env_len = len - dollar_sign_matched_index;
|
||||
size_t value_len = strlen(value);
|
||||
ssize_t len_diff = value_len - env_len;
|
||||
|
||||
substitute(value, value_len, dollar_sign_matched_index, len, len_diff, ba_length, arr, &used_bytes, exceeded, &exceeded_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exceeded_len > 0) {
|
||||
struct file_data fd = make_file_data(exceeded, exceeded_len, ba_length);
|
||||
hmput(files_map, hash, fd);
|
||||
}
|
||||
memcpy(ba, arr, ba_length);
|
||||
}
|
||||
(*env)->ReleaseByteArrayElements(env, buf, ba, 0);
|
||||
return used_bytes;
|
||||
}
|
||||
|
||||
static void open0_hook(JNIEnv* env, jobject thiz, jstring jpath) {
|
||||
if (!jpath) {
|
||||
return;
|
||||
}
|
||||
const char* path = (*env)->GetStringUTFChars(env, jpath, NULL);
|
||||
if (path) {
|
||||
if (is_config_path(path)) {
|
||||
tag(thiz);
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars(env, jpath, path);
|
||||
}
|
||||
real_open0(env, thiz, jpath);
|
||||
}
|
||||
|
||||
static void dealloc(jvmtiEnv* jvmti, char* p) {
|
||||
if (p != NULL) {
|
||||
unsigned char* up = (unsigned char*)p;
|
||||
(*jvmti)->Deallocate(jvmti, up);
|
||||
}
|
||||
}
|
||||
|
||||
static void JNICALL onNativeMethodBind(jvmtiEnv* jvmti_env, JNIEnv* env, jthread thread, jmethodID method, void* address, void** new_address_ptr) {
|
||||
char* mname = NULL;
|
||||
char* msig = NULL;
|
||||
char* csig = NULL;
|
||||
char* gsig = NULL;
|
||||
|
||||
jclass decl;
|
||||
(*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &decl);
|
||||
(*jvmti_env)->GetClassSignature(jvmti_env, decl, &csig, &gsig);
|
||||
(*jvmti_env)->GetMethodName(jvmti_env, method, &mname, &msig, NULL);
|
||||
|
||||
if (csig && strcmp(csig, "Ljava/io/FileInputStream;") == 0 && mname && msig) {
|
||||
if (strcmp(mname, "open0") == 0 && strcmp(msig, "(Ljava/lang/String;)V") == 0) {
|
||||
real_open0 = (open0_t)address;
|
||||
*new_address_ptr = (void*)&open0_hook;
|
||||
} else if (strcmp(mname, "readBytes") == 0 && strcmp(msig, "([BII)I") == 0) {
|
||||
real_readBytes = (readBytes_t)address;
|
||||
*new_address_ptr = (void*)&readBytes_hook;
|
||||
}
|
||||
}
|
||||
|
||||
(*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)mname);
|
||||
(*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)msig);
|
||||
(*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)csig);
|
||||
(*jvmti_env)->Deallocate(jvmti_env, (unsigned char*)gsig);
|
||||
}
|
||||
|
||||
static jint init_jvmti(JavaVM* vm) {
|
||||
jint rc = (*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_2);
|
||||
if (rc != JNI_OK || jvmti == NULL) return JNI_ERR;
|
||||
|
||||
jvmtiCapabilities caps;
|
||||
memset(&caps, 0, sizeof(caps));
|
||||
caps.can_generate_native_method_bind_events = 1;
|
||||
caps.can_tag_objects = 1;
|
||||
(*jvmti)->AddCapabilities(jvmti, &caps);
|
||||
|
||||
jvmtiEventCallbacks callbacks;
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.NativeMethodBind = &onNativeMethodBind;
|
||||
(*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
|
||||
|
||||
(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_NATIVE_METHOD_BIND, (jthread)NULL);
|
||||
|
||||
FILE *env_file = fopen(".env", "r");
|
||||
if (env_file) {
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t n;
|
||||
while ((n = getline(&line, &len, env_file)) != -1) {
|
||||
if (n > 0 && line[n - 1] == '\n') {
|
||||
line[n - 1] = '\0';
|
||||
}
|
||||
char *name = line;
|
||||
char *value = NULL;
|
||||
char *equal = strchr(line, '=');
|
||||
if (equal == NULL) {
|
||||
fprintf(stderr, "\e[0;31m[SubstAgent] Invalid line in .env: \"%s\"\e[0m\n", line);
|
||||
continue;
|
||||
}
|
||||
*equal = '\0';
|
||||
value = equal + 1;
|
||||
if (setenv(name, value, 0) != 0) {
|
||||
perror("setenv failed");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (errno != ENOENT) {
|
||||
perror("[SubstAgent] fopen failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "[SubstAgent] Loaded version %s\n", GIT_HASH);
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
|
||||
return init_jvmti(vm);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
|
||||
return init_jvmti(vm);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
SAME_LENGTH_ENV=abcdefghijklmno
|
||||
SHORTER_ENV=abcdefghijklmnopqrstuvwxyz
|
||||
LONGER_ENV=abcdef
|
||||
@@ -0,0 +1,40 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $SAME_LENGTH_ENV where everything fits in one byte array
|
||||
class Test1 {
|
||||
static String correctString = "Hello abcdefghijklmno";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test1.txt");
|
||||
byte[] buf = new byte[256];
|
||||
int n = fis.read(buf);
|
||||
byte[] content = Arrays.copyOfRange(buf, 0, n);
|
||||
String contentString = new String(content);
|
||||
if (contentString.equals(correctString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("Content string was \"%s\", should be \"%s\"\n", contentString, correctString);
|
||||
System.out.printf("Content string hex: \"%s\"\n", toHexDump(contentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $SAME_LENGTH_ENV at the end of the file
|
||||
class Test10 {
|
||||
static String correctString = "According to all known laws of aviation, there is no way a bee should be able to fly abcdefghijklmno";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test10.txt");
|
||||
byte[] buf = new byte[101];
|
||||
int n = fis.read(buf);
|
||||
byte[] content = Arrays.copyOfRange(buf, 0, n);
|
||||
String contentString = new String(content);
|
||||
|
||||
if (contentString.equals(correctString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("Content string was \"%s\", should be \"%s\"\n", contentString, correctString);
|
||||
System.out.printf("Content string hex: \"%s\"\n", toHexDump(contentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $SHORTER_ENV at the end of the file
|
||||
class Test11 {
|
||||
static String correctString = "According to all known laws of aviation, there is no way a bee should be able to fly abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test11.txt");
|
||||
byte[] buf = new byte[111];
|
||||
int n = fis.read(buf);
|
||||
byte[] content = Arrays.copyOfRange(buf, 0, n);
|
||||
String contentString = new String(content);
|
||||
|
||||
if (contentString.equals(correctString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("Content string was \"%s\", should be \"%s\"\n", contentString, correctString);
|
||||
System.out.printf("Content string hex: \"%s\"\n", toHexDump(contentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test overflowing $SHORTER_ENV
|
||||
class Test12 {
|
||||
static String correctFirstString = "abcdefghijkl";
|
||||
static String correctSecondString = "mnopqrstuvwxyz";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test12.txt");
|
||||
byte[] firstBuf = new byte[12];
|
||||
int firstN = fis.read(firstBuf);
|
||||
byte[] firstContent = Arrays.copyOfRange(firstBuf, 0, firstN);
|
||||
String firstContentString = new String(firstContent);
|
||||
|
||||
byte[] secondBuf = new byte[256];
|
||||
int secondN = fis.read(secondBuf);
|
||||
byte[] secondContent = Arrays.copyOfRange(secondBuf, 0, secondN);
|
||||
String secondContentString = new String(secondContent);
|
||||
|
||||
if (firstContentString.equals(correctFirstString) && secondContentString.equals(secondContentString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("First content string was \"%s\", should be \"%s\"\n", firstContentString, correctFirstString);
|
||||
System.out.printf("First content string hex: \"%s\"\n", toHexDump(firstContentString));
|
||||
System.out.printf("Second content string was \"%s\", should be \"%s\"\n", secondContentString, correctSecondString);
|
||||
System.out.printf("Second content string hex: \"%s\"\n", toHexDump(secondContentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test file that is not a config file
|
||||
class Test13 {
|
||||
static String correctString = "This is not a config file so $SAME_LENGTH_ENV shouldn't be substituted";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test13.nacf");
|
||||
byte[] buf = new byte[256];
|
||||
int n = fis.read(buf);
|
||||
byte[] content = Arrays.copyOfRange(buf, 0, n);
|
||||
String contentString = new String(content);
|
||||
|
||||
if (contentString.equals(correctString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("Content string was \"%s\", should be \"%s\"\n", contentString, correctString);
|
||||
System.out.printf("Content string hex: \"%s\"\n", toHexDump(contentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
// Test reading really big file
|
||||
class Test14 {
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test14.txt");
|
||||
byte[] buf = new byte[2097152];
|
||||
fis.read(buf);
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $SHORTER_ENV where everything fits in one byte array
|
||||
class Test2 {
|
||||
static String correctString = "Hello abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test2.txt");
|
||||
byte[] buf = new byte[256];
|
||||
int n = fis.read(buf);
|
||||
byte[] content = Arrays.copyOfRange(buf, 0, n);
|
||||
String contentString = new String(content);
|
||||
if (contentString.equals(correctString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("Content string was \"%s\", should be \"%s\"\n", contentString, correctString);
|
||||
System.out.printf("Content string hex: \"%s\"\n", toHexDump(contentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $LONGER_ENV where everything fits in one byte array
|
||||
class Test3 {
|
||||
static String correctString = "Hello abcdef";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test3.txt");
|
||||
byte[] buf = new byte[256];
|
||||
int n = fis.read(buf);
|
||||
byte[] content = Arrays.copyOfRange(buf, 0, n);
|
||||
String contentString = new String(content);
|
||||
if (contentString.equals(correctString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("Content string was \"%s\", should be \"%s\"\n", contentString, correctString);
|
||||
System.out.printf("Content string hex: \"%s\"\n", toHexDump(contentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $SAME_LENGTH_ENV split in 2 byte arrays, only the read content overflows
|
||||
class Test4 {
|
||||
static String correctFirstString = "Hi abcdefghijklmno ";
|
||||
static String correctSecondString = "According to all known laws of aviation, there is no way a bee should be able to fly";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test4.txt");
|
||||
byte[] firstBuf = new byte[20];
|
||||
int firstN = fis.read(firstBuf);
|
||||
byte[] firstContent = Arrays.copyOfRange(firstBuf, 0, firstN);
|
||||
String firstContentString = new String(firstContent);
|
||||
|
||||
byte[] secondBuf = new byte[256];
|
||||
int secondN = fis.read(secondBuf);
|
||||
byte[] secondContent = Arrays.copyOfRange(secondBuf, 0, secondN);
|
||||
String secondContentString = new String(secondContent);
|
||||
|
||||
if (firstContentString.equals(correctFirstString) && secondContentString.equals(correctSecondString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.printf("Incorrect output. Was \"%s\" should be \"%s\"\n", firstContentString + secondContentString, correctFirstString + correctSecondString);
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $SHORTER_ENV split in 2 byte arrays, only the read content overflows
|
||||
class Test5 {
|
||||
static String correctFirstString = "Hi abcdefghijklmnopqrstuvwxyz ";
|
||||
static String correctSecondString = "According to all known laws of aviation, there is no way a bee should be able to fly";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test5.txt");
|
||||
byte[] firstBuf = new byte[30];
|
||||
int firstN = fis.read(firstBuf);
|
||||
byte[] firstContent = Arrays.copyOfRange(firstBuf, 0, firstN);
|
||||
String firstContentString = new String(firstContent);
|
||||
|
||||
byte[] secondBuf = new byte[256];
|
||||
int secondN = fis.read(secondBuf);
|
||||
byte[] secondContent = Arrays.copyOfRange(secondBuf, 0, secondN);
|
||||
String secondContentString = new String(secondContent);
|
||||
|
||||
if (firstContentString.equals(correctFirstString) && secondContentString.equals(correctSecondString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("First content string was \"%s\", should be \"%s\"\n", firstContentString, correctFirstString);
|
||||
System.out.printf("First content string hex: \"%s\"\n", toHexDump(firstContentString));
|
||||
System.out.printf("Second content string was \"%s\", should be \"%s\"\n", secondContentString, correctSecondString);
|
||||
System.out.printf("Second content string hex: \"%s\"\n", toHexDump(secondContentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $LONGER_ENV split in 2 byte arrays, only the read content overflows
|
||||
class Test6 {
|
||||
static String correctFirstString = "Hi abcdef ";
|
||||
static String correctSecondString = "According to all known laws of aviation, there is no way a bee should be able to fly";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test6.txt");
|
||||
byte[] firstBuf = new byte[15];
|
||||
int firstN = fis.read(firstBuf);
|
||||
byte[] firstContent = Arrays.copyOfRange(firstBuf, 0, firstN);
|
||||
String firstContentString = new String(firstContent);
|
||||
|
||||
byte[] secondBuf = new byte[256];
|
||||
int secondN = fis.read(secondBuf);
|
||||
byte[] secondContent = Arrays.copyOfRange(secondBuf, 0, secondN);
|
||||
String secondContentString = new String(secondContent);
|
||||
|
||||
if (firstContentString.equals(correctFirstString) && secondContentString.equals(correctSecondString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("First content string was \"%s\", should be \"%s\"\n", firstContentString, correctFirstString);
|
||||
System.out.printf("First content string hex: \"%s\"\n", toHexDump(firstContentString));
|
||||
System.out.printf("Second content string was \"%s\", should be \"%s\"\n", secondContentString, correctSecondString);
|
||||
System.out.printf("Second content string hex: \"%s\"\n", toHexDump(secondContentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $SAME_LENGTH_ENV split in 3 byte arrays, the entirety of the read content overflows
|
||||
class Test7 {
|
||||
static String correctFirstString = "Hi abcdefghijklmno ";
|
||||
static String correctSecondString = "According ";
|
||||
static String correctThirdString = "to all known laws of aviation, there is no way a bee should be able to fly";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test7.txt");
|
||||
byte[] firstBuf = new byte[20];
|
||||
int firstN = fis.read(firstBuf);
|
||||
byte[] firstContent = Arrays.copyOfRange(firstBuf, 0, firstN);
|
||||
String firstContentString = new String(firstContent);
|
||||
|
||||
byte[] secondBuf = new byte[10];
|
||||
int secondN = fis.read(secondBuf);
|
||||
byte[] secondContent = Arrays.copyOfRange(secondBuf, 0, secondN);
|
||||
String secondContentString = new String(secondContent);
|
||||
|
||||
byte[] thirdBuf = new byte[256];
|
||||
int thirdN = fis.read(thirdBuf);
|
||||
byte[] thirdContent = Arrays.copyOfRange(thirdBuf, 0, thirdN);
|
||||
String thirdContentString = new String(thirdContent);
|
||||
|
||||
if (firstContentString.equals(correctFirstString) && secondContentString.equals(correctSecondString) && thirdContentString.equals(correctThirdString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("First content string was \"%s\", should be \"%s\"\n", firstContentString, correctFirstString);
|
||||
System.out.printf("First content string hex: \"%s\"\n", toHexDump(firstContentString));
|
||||
System.out.printf("Second content string was \"%s\", should be \"%s\"\n", secondContentString, correctSecondString);
|
||||
System.out.printf("Second content string hex: \"%s\"\n", toHexDump(secondContentString));
|
||||
System.out.printf("Third content string was \"%s\", should be \"%s\"\n", thirdContentString, correctThirdString);
|
||||
System.out.printf("Third content string hex: \"%s\"\n", toHexDump(thirdContentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $SHORTER_ENV split in 3 byte arrays, the entirety of the read content overflows
|
||||
class Test8 {
|
||||
static String correctFirstString = "Hi abcdefghijklmnopqrstuvwxyz ";
|
||||
static String correctSecondString = "According ";
|
||||
static String correctThirdString = "to all known laws of aviation, there is no way a bee should be able to fly";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test5.txt");
|
||||
byte[] firstBuf = new byte[30];
|
||||
int firstN = fis.read(firstBuf);
|
||||
byte[] firstContent = Arrays.copyOfRange(firstBuf, 0, firstN);
|
||||
String firstContentString = new String(firstContent);
|
||||
|
||||
byte[] secondBuf = new byte[10];
|
||||
int secondN = fis.read(secondBuf);
|
||||
byte[] secondContent = Arrays.copyOfRange(secondBuf, 0, secondN);
|
||||
String secondContentString = new String(secondContent);
|
||||
|
||||
byte[] thirdBuf = new byte[256];
|
||||
int thirdN = fis.read(thirdBuf);
|
||||
byte[] thirdContent = Arrays.copyOfRange(thirdBuf, 0, thirdN);
|
||||
String thirdContentString = new String(thirdContent);
|
||||
|
||||
if (firstContentString.equals(correctFirstString) && secondContentString.equals(correctSecondString) && thirdContentString.equals(correctThirdString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("First content string was \"%s\", should be \"%s\"\n", firstContentString, correctFirstString);
|
||||
System.out.printf("First content string hex: \"%s\"\n", toHexDump(firstContentString));
|
||||
System.out.printf("Second content string was \"%s\", should be \"%s\"\n", secondContentString, correctSecondString);
|
||||
System.out.printf("Second content string hex: \"%s\"\n", toHexDump(secondContentString));
|
||||
System.out.printf("Third content string was \"%s\", should be \"%s\"\n", thirdContentString, correctThirdString);
|
||||
System.out.printf("Third content string hex: \"%s\"\n", toHexDump(thirdContentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
// Test $LONGER_ENV split in 3 byte arrays, the entirety of the read content overflows
|
||||
class Test9 {
|
||||
static String correctFirstString = "Hi abcdef ";
|
||||
static String correctSecondString = "According ";
|
||||
static String correctThirdString = "to all known laws of aviation, there is no way a bee should be able to fly";
|
||||
|
||||
public static String toHexDump(String s) {
|
||||
byte[] bytes = s.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 3);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(String.format("%02X ", bytes[i] & 0xFF));
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public static void main(String[] args) throws IOException {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream("../data/test9.txt");
|
||||
byte[] firstBuf = new byte[15];
|
||||
int firstN = fis.read(firstBuf);
|
||||
byte[] firstContent = Arrays.copyOfRange(firstBuf, 0, firstN);
|
||||
String firstContentString = new String(firstContent);
|
||||
|
||||
byte[] secondBuf = new byte[10];
|
||||
int secondN = fis.read(secondBuf);
|
||||
byte[] secondContent = Arrays.copyOfRange(secondBuf, 0, secondN);
|
||||
String secondContentString = new String(secondContent);
|
||||
|
||||
byte[] thirdBuf = new byte[256];
|
||||
int thirdN = fis.read(thirdBuf);
|
||||
byte[] thirdContent = Arrays.copyOfRange(thirdBuf, 0, thirdN);
|
||||
String thirdContentString = new String(thirdContent);
|
||||
|
||||
if (firstContentString.equals(correctFirstString) && secondContentString.equals(correctSecondString) && thirdContentString.equals(correctThirdString)) {
|
||||
System.err.println("Correct output");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("=== Incorrect output ===");
|
||||
System.out.printf("First content string was \"%s\", should be \"%s\"\n", firstContentString, correctFirstString);
|
||||
System.out.printf("First content string hex: \"%s\"\n", toHexDump(firstContentString));
|
||||
System.out.printf("Second content string was \"%s\", should be \"%s\"\n", secondContentString, correctSecondString);
|
||||
System.out.printf("Second content string hex: \"%s\"\n", toHexDump(secondContentString));
|
||||
System.out.printf("Third content string was \"%s\", should be \"%s\"\n", thirdContentString, correctThirdString);
|
||||
System.out.printf("Third content string hex: \"%s\"\n", toHexDump(thirdContentString));
|
||||
System.out.println("========================");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Hello $SAME_LENGTH_ENV
|
||||
@@ -0,0 +1 @@
|
||||
According to all known laws of aviation, there is no way a bee should be able to fly $SAME_LENGTH_ENV
|
||||
@@ -0,0 +1 @@
|
||||
According to all known laws of aviation, there is no way a bee should be able to fly $SHORTER_ENV
|
||||
@@ -0,0 +1 @@
|
||||
$SHORTER_ENV
|
||||
@@ -0,0 +1 @@
|
||||
This is not a config file so $SAME_LENGTH_ENV shouldn't be substituted
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1 @@
|
||||
Hello $SHORTER_ENV
|
||||
@@ -0,0 +1 @@
|
||||
Hello $LONGER_ENV
|
||||
@@ -0,0 +1 @@
|
||||
Hi $SAME_LENGTH_ENV According to all known laws of aviation, there is no way a bee should be able to fly
|
||||
@@ -0,0 +1 @@
|
||||
Hi $SHORTER_ENV According to all known laws of aviation, there is no way a bee should be able to fly
|
||||
@@ -0,0 +1 @@
|
||||
Hi $LONGER_ENV According to all known laws of aviation, there is no way a bee should be able to fly
|
||||
@@ -0,0 +1 @@
|
||||
Hi $SAME_LENGTH_ENV According to all known laws of aviation, there is no way a bee should be able to fly
|
||||
@@ -0,0 +1 @@
|
||||
Hi $SHORTER_ENV According to all known laws of aviation, there is no way a bee should be able to fly
|
||||
@@ -0,0 +1 @@
|
||||
Hi $LONGER_ENV According to all known laws of aviation, there is no way a bee should be able to fly
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
set -u
|
||||
TESTS_COUNT=14
|
||||
TEST_DESCRIPTIONS=(
|
||||
"\$SAME_LENGTH_ENV where everything fits in one byte array"
|
||||
"\$SHORTER_ENV where everything fits in one byte array"
|
||||
"\$LONGER_ENV where everything fits in one byte array"
|
||||
"\$SAME_LENGTH_ENV split in 2 byte arrays, only the read content overflows"
|
||||
"\$SHORTER_ENV split in 2 byte arrays, only the read content overflows"
|
||||
"\$LONGER_ENV split in 2 byte arrays, only the read content overflows"
|
||||
"\$SAME_LENGTH_ENV split in 3 byte arrays, the entirety of the read content overflows"
|
||||
"\$SHORTER_ENV split in 3 byte arrays, the entirety of the read content overflows"
|
||||
"\$LONGER_ENV split in 3 byte arrays, the entirety of the read content overflows"
|
||||
"\$SAME_LENGTH_ENV at the end of the file"
|
||||
"\$SHORTER_ENV at the end of the file"
|
||||
"Overflowing \$SHORTER_ENV"
|
||||
"File that is not a config file"
|
||||
"Reading really big file"
|
||||
)
|
||||
|
||||
# Build SubstAgent
|
||||
echo -n "Compiling SubstAgent..."
|
||||
cd ../build
|
||||
output=$(make 2>&1)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "\e[0;31mBuild failed\e[0m"
|
||||
echo "$output"
|
||||
exit 1
|
||||
fi
|
||||
echo -ne "\e[2K\r"
|
||||
echo "Done compiling SubstAgent"
|
||||
cd ../tests
|
||||
|
||||
substagent_path=$(realpath "../build/libsubstagent.so")
|
||||
|
||||
# Cleanup
|
||||
rm -rf ./build
|
||||
mkdir ./build
|
||||
|
||||
# Compile tests
|
||||
echo -n "Compiling test "
|
||||
tests_count_digits=${#TESTS_COUNT}
|
||||
for (( TEST=1; TEST<=TESTS_COUNT; TEST++ )); do
|
||||
echo -n "$TEST/$TESTS_COUNT"
|
||||
javac Test$TEST.java -d ./build
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "\e[0;31mCould not compile test $TEST\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
test_digits=${#TEST}
|
||||
shift_left=$(( tests_count_digits + test_digits + 1 ))
|
||||
echo -ne "\e[${shift_left}D"
|
||||
done
|
||||
echo -ne "\e[2K\r"
|
||||
echo "Done compiling tests"
|
||||
|
||||
cp .env ./build
|
||||
|
||||
# Run tests
|
||||
cd ./build
|
||||
|
||||
a_test_has_failed=false
|
||||
for (( TEST=1; TEST<=TESTS_COUNT; TEST++ )); do
|
||||
output=$(java -agentpath:$substagent_path Test$TEST 2>&1)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "\e[0;32m✓ Test $TEST passed - ${TEST_DESCRIPTIONS[$TEST-1]}\e[0m"
|
||||
else
|
||||
echo -e "\e[0;31m✗ Test $TEST failed - ${TEST_DESCRIPTIONS[$TEST-1]}\e[0m"
|
||||
a_test_has_failed=true
|
||||
echo "$output"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "=================="
|
||||
if [ "$a_test_has_failed" == true ] ; then
|
||||
echo -e "\e[0;31m✗ One or more tests failed\e[0m"
|
||||
else
|
||||
echo -e "\e[0;32m✓ All tests passed\e[0m"
|
||||
fi
|
||||
Reference in new issue
Block a user