diff --git a/.github/codecov.yml b/.github/codecov.yml
new file mode 100644
index 000000000..efb6fccad
--- /dev/null
+++ b/.github/codecov.yml
@@ -0,0 +1,21 @@
+codecov:
+ branch: main
+ require_ci_to_pass: yes
+
+coverage:
+ precision: 2
+ round: down
+ range: "85...100"
+
+parsers:
+ gcov:
+ branch_detection:
+ conditional: yes
+ loop: yes
+ method: no
+ macro: no
+
+comment:
+ layout: "reach,diff,flags,files,footer"
+ behavior: default
+ require_changes: no
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index 5563d1a0b..23194dcd1 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -21,9 +21,8 @@ jobs:
- name: Build Debug Build
run: cmake --build cmake-build-debug
- name: Run Tests
- run: |
- cd cmake-build-debug/src/tests/
- ./omega_test -d yes --order lex
+ working-directory: cmake-build-debug/src/tests/
+ run: ./omega_test -d yes --order lex
- name: Create Node v10 Virtual Environment
run: nodeenv --node=10.24.1 venv
- name: Prepare To Build Node v10 Bindings
@@ -46,17 +45,18 @@ jobs:
with:
fetch-depth: 0
- name: Setup Debug Build
- run: cmake -S . -B cmake-build-debug
+ run: cmake -S . -B cmake-build-debug -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS=--coverage -DCMAKE_C_FLAGS=--coverage
- name: Build Debug Build
run: cmake --build cmake-build-debug
- name: Run Tests
- run: |
- cd cmake-build-debug/src/tests/
- ./omega_test -d yes --order lex
+ working-directory: cmake-build-debug/src/tests/
+ run: ./omega_test -d yes --order lex
+ - name: Collect code coverage
+ working-directory: cmake-build-debug/src/tests/
+ run: bash <(curl -s https://codecov.io/bash)
- name: Run Tests Under Valgrind
- run: |
- cd cmake-build-debug/src/tests/
- valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./omega_test -d yes --order lex
+ working-directory: cmake-build-debug/src/tests/
+ run: valgrind --leak-check=full --show-leak-kinds=all --leak-resolution=med --track-origins=yes --vgdb=no --error-exitcode=1 ./omega_test -d yes --order lex
- name: Create Node v10 Virtual Environment
run: nodeenv --node=10.24.1 venv
- name: Prepare To Build Node v10 Bindings
diff --git a/README.md b/README.md
index c3b0c8402..0a896707e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
# Ωedit Library
![Build Status](https://github.com/scholarsmate/omega-edit/workflows/Unit%20Tests/badge.svg)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fscholarsmate%2Fomega-edit.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fscholarsmate%2Fomega-edit?ref=badge_shield)
+[![codecov](https://codecov.io/gh/scholarsmate/omega-edit/branch/master/graph/badge.svg)](https://codecov.io/gh/scholarsmate/omega-edit)
The goal of this project is to provide an open source library for building editors that can handle massive files, multiple authors, and multiple viewports.
@@ -12,7 +13,7 @@ If you are using just the command line you will need these things installed:
- C/C++ compiler (such as clang, gcc, or mingw)
- CMake (https://cmake.org/download/)
- make or ninja
-- nodeenv
+- nvm or nodeenv
If developing the Ωedit API, you'll need SWIG installed as well.
diff --git a/src/lib/edit.cpp b/src/lib/edit.cpp
index 9799dc80e..30e761e38 100644
--- a/src/lib/edit.cpp
+++ b/src/lib/edit.cpp
@@ -142,6 +142,24 @@ static omega_model_segment_ptr_t clone_model_segment_(const omega_model_segment_
return result;
}
+static inline void free_session_changes_(omega_session_t *session_ptr) {
+ for (auto &change_ptr : session_ptr->model_ptr_->changes) {
+ if (change_ptr->kind != change_kind_t::CHANGE_DELETE && 7 < change_ptr->length) {
+ delete[] const_cast(change_ptr.get())->data.bytes_ptr;
+ }
+ }
+ session_ptr->model_ptr_->changes.clear();
+}
+
+static inline void free_session_changes_undone_(omega_session_t *session_ptr) {
+ for (auto &change_ptr : session_ptr->model_ptr_->changes_undone) {
+ if (change_ptr->kind != change_kind_t::CHANGE_DELETE && 7 < change_ptr->length) {
+ delete[] const_cast(change_ptr.get())->data.bytes_ptr;
+ }
+ }
+ session_ptr->model_ptr_->changes_undone.clear();
+}
+
/* --------------------------------------------------------------------------------------------------------------------
The objective here is to model the edits using segments. Essentially creating a contiguous model of the file by
keeping track of what to do. The verbs here are READ, INSERT, and OVERWRITE. We don't need to model DELETE because
@@ -151,14 +169,16 @@ static omega_model_segment_ptr_t clone_model_segment_(const omega_model_segment_
static int update_model_helper_(omega_model_t *model_ptr, const_omega_change_ptr_t &change_ptr) {
int64_t read_offset = 0;
- if (model_ptr->model_segments.empty() && change_ptr->kind != change_kind_t::CHANGE_DELETE) {
- // The model is empty, and we have a change with content
- auto insert_segment_ptr = std::make_unique();
- insert_segment_ptr->computed_offset = change_ptr->offset;
- insert_segment_ptr->computed_length = change_ptr->length;
- insert_segment_ptr->change_offset = 0;
- insert_segment_ptr->change_ptr = change_ptr;
- model_ptr->model_segments.push_back(std::move(insert_segment_ptr));
+ if (model_ptr->model_segments.empty()) {
+ if (change_ptr->kind != change_kind_t::CHANGE_DELETE) {
+ // The model is empty, and we have a change with content
+ auto insert_segment_ptr = std::make_unique();
+ insert_segment_ptr->computed_offset = change_ptr->offset;
+ insert_segment_ptr->computed_length = change_ptr->length;
+ insert_segment_ptr->change_offset = 0;
+ insert_segment_ptr->change_ptr = change_ptr;
+ model_ptr->model_segments.push_back(std::move(insert_segment_ptr));
+ }
return 0;
}
for (auto iter = model_ptr->model_segments.begin(); iter != model_ptr->model_segments.end(); ++iter) {
@@ -253,7 +273,7 @@ static int64_t update_(omega_session_t *session_ptr, const_omega_change_ptr_t ch
undone_change_ptr->serial *= -1;
} else if (!session_ptr->model_ptr_->changes_undone.empty()) {
// This is not a redo change, so any changes undone are now invalid and must be cleared
- session_ptr->model_ptr_->changes_undone.clear();
+ free_session_changes_undone_(session_ptr);
}
session_ptr->model_ptr_->changes.push_back(change_ptr);
if (0 != update_model_(session_ptr->model_ptr_.get(), change_ptr)) { return -1; }
@@ -289,11 +309,8 @@ omega_session_t *omega_edit_create_session(const char *file_path, omega_session_
void omega_edit_destroy_session(omega_session_t *session_ptr) {
if (session_ptr->file_ptr) { fclose(session_ptr->file_ptr); }
while (!session_ptr->viewports_.empty()) { omega_edit_destroy_viewport(session_ptr->viewports_.back().get()); }
- for (auto &change_ptr : session_ptr->model_ptr_->changes) {
- if (change_ptr->kind != change_kind_t::CHANGE_DELETE && 7 < change_ptr->length) {
- delete[] const_cast(change_ptr.get())->data.bytes_ptr;
- }
- }
+ free_session_changes_(session_ptr);
+ free_session_changes_undone_(session_ptr);
delete session_ptr;
}
@@ -349,7 +366,7 @@ int64_t omega_edit_insert(omega_session_t *session_ptr, int64_t offset, const ch
int64_t omega_edit_overwrite_bytes(omega_session_t *session_ptr, int64_t offset, const omega_byte_t *bytes,
int64_t length) {
- return (0 <= length && offset < omega_session_get_computed_file_size(session_ptr))
+ return (0 <= length && offset <= omega_session_get_computed_file_size(session_ptr))
? update_(session_ptr, ovr_(1 + static_cast(omega_session_get_num_changes(session_ptr)),
offset, bytes, length))
: 0;
@@ -405,7 +422,7 @@ int omega_edit_clear_changes(omega_session_t *session_ptr) {
length = ftello(session_ptr->file_ptr);
}
initialize_model_segments_(session_ptr->model_ptr_->model_segments, length);
- session_ptr->model_ptr_->changes.clear();
+ free_session_changes_(session_ptr);
for (const auto &viewport_ptr : session_ptr->viewports_) {
viewport_ptr->data_segment.capacity = -1 * std::abs(viewport_ptr->data_segment.capacity);// indicate dirty read
omega_viewport_execute_on_change(viewport_ptr.get(), nullptr);
diff --git a/src/tests/omega_test.cpp b/src/tests/omega_test.cpp
index 44f85f31e..2abaca906 100644
--- a/src/tests/omega_test.cpp
+++ b/src/tests/omega_test.cpp
@@ -50,11 +50,9 @@ TEST_CASE("Buffer Shift", "[BufferShift]") {
auto buff_len = (int64_t) strlen((const char *) buffer);
// Shift the buffer 3 bits to the right
- auto rc = omega_util_right_shift_buffer(buffer, buff_len, 3);
- REQUIRE(rc == 0);
+ REQUIRE(0 == omega_util_right_shift_buffer(buffer, buff_len, 3));
// Shift the buffer 5 bits to the right
- rc = omega_util_right_shift_buffer(buffer, buff_len, 5);
- REQUIRE(rc == 0);
+ REQUIRE(0 == omega_util_right_shift_buffer(buffer, buff_len, 5));
// We shifted a total of 8 bits (one byte) to the right, so compare the buffer against the fill plus one byte
REQUIRE(strcmp((const char *) fill + 1, (const char *) buffer) == 0);
@@ -63,14 +61,16 @@ TEST_CASE("Buffer Shift", "[BufferShift]") {
REQUIRE(strcmp((const char *) fill, (const char *) buffer) == 0);
// Shift the buffer 6 bits to the left
- rc = omega_util_left_shift_buffer(buffer, buff_len, 6);
- REQUIRE(rc == 0);
+ REQUIRE(0 == omega_util_left_shift_buffer(buffer, buff_len, 6));
// Shift the buffer 2 bits to the left
- rc = omega_util_left_shift_buffer(buffer, buff_len, 2);
- REQUIRE(0 == rc);
+ REQUIRE(0 == omega_util_left_shift_buffer(buffer, buff_len, 2));
// We shifted a total of 8 bits (one byte) to the left, so compare the buffer against the fill plus one byte
REQUIRE(strcmp((const char *) fill + 1, (const char *) buffer) == 0);
+ // Negative tests. Shifting 8 or more bits in either direction should be an error.
+ REQUIRE(-1 == omega_util_left_shift_buffer(buffer, buff_len, 8));
+ REQUIRE(-1 == omega_util_right_shift_buffer(buffer, buff_len, 8));
+
free(buffer);
}
@@ -85,6 +85,17 @@ TEST_CASE("File Compare", "[UtilTests]") {
}
}
+TEST_CASE("File Exists", "[UtilTests]") {
+ REQUIRE(omega_util_file_exists("data/test1.dat"));
+ REQUIRE(!omega_util_file_exists("data/IDonTExist.DaT"));
+}
+
+TEST_CASE("Current Directory", "[UtilTests]") {
+ using Catch::Matchers::Contains;
+ using Catch::Matchers::EndsWith;
+ REQUIRE_THAT(omega_util_get_current_dir(), Contains("src") && EndsWith("tests"));
+}
+
static inline omega_byte_t to_lower(omega_byte_t byte) { return tolower(byte); }
static inline omega_byte_t to_upper(omega_byte_t byte) { return toupper(byte); }
@@ -148,9 +159,16 @@ TEST_CASE("Empty File Test", "[EmptyFileTests]") {
REQUIRE(session_ptr);
REQUIRE(strcmp(omega_session_get_file_path(session_ptr), in_filename) == 0);
REQUIRE(omega_session_get_computed_file_size(session_ptr) == file_size);
- REQUIRE(0 < omega_edit_insert_bytes(session_ptr, 0, reinterpret_cast("0"), 0));
- file_size += 1;
- REQUIRE(omega_session_get_computed_file_size(session_ptr) == file_size);
+ REQUIRE(0 == omega_edit_undo_last_change(session_ptr));
+ auto change_serial = omega_edit_insert_bytes(session_ptr, 0, reinterpret_cast("1234567890"), 0);
+ REQUIRE(0 < change_serial);
+ file_size += 10;
+ REQUIRE(file_size == omega_session_get_computed_file_size(session_ptr));
+ REQUIRE((change_serial * -1) == omega_edit_undo_last_change(session_ptr));
+ REQUIRE(0 == omega_session_get_computed_file_size(session_ptr));
+ change_serial = omega_edit_overwrite_string(session_ptr, 0, "abcdefghhijklmnopqrstuvwxyz");
+ REQUIRE(0 < change_serial);
+ REQUIRE(27 == omega_session_get_computed_file_size(session_ptr));
omega_edit_destroy_session(session_ptr);
}