diff --git a/.gitignore b/.gitignore index 234bd3c4550c07f52be5458bc10ea92d4019b870..c23dfde0164c2ce71be3a2113f47b2f96e793753 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,9 @@ *.toc *.blg +# cmake +CMakeFiles +CMakeCache.txt +cmake_install.cmake +Makefile + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..717eb124c4eb1e08d455bdf73446fabec96a6265 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required (VERSION 2.8) + +# project info +project (cell_algorithms) +enable_language(CXX) + +# compilation flags +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread -Wall") + +# this generates a .json file with full compilation command for each file +set(CMAKE_EXPORT_COMPILE_COMMANDS "YES") + +# generated .a and .so go into /lib +#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +include_directories(${CMAKE_SOURCE_DIR}/src) +include_directories(${CMAKE_SOURCE_DIR}) + +add_subdirectory(tests) + diff --git a/cells_small.json b/data/cells_small.json similarity index 100% rename from cells_small.json rename to data/cells_small.json diff --git a/makefile b/makefile deleted file mode 100644 index 736a149f83bcb53f082a9a3656d7f1ef5ca5f321..0000000000000000000000000000000000000000 --- a/makefile +++ /dev/null @@ -1,13 +0,0 @@ -CC=clang++ -FLAGS=-std=c++11 -g -pedantic - -test.exe : main.cpp *.hpp makefile gtest.o - $(CC) $(FLAGS) main.cpp -o test.exe gtest.o -pthread - -gtest.o : - $(CC) $(FLAGS) gtest-all.cc -c -o gtest.o - -clean : - rm -f test.exe - rm -f gtest.o - rm -f a.out diff --git a/cell_tree.hpp b/src/cell_tree.hpp similarity index 99% rename from cell_tree.hpp rename to src/cell_tree.hpp index 3daf48d56620725dae2bb44cb47dfe318b4ab0c5..e6ba5b2e9dcb4c19e1708bf7d87554f47cfb5f94 100644 --- a/cell_tree.hpp +++ b/src/cell_tree.hpp @@ -192,7 +192,6 @@ class cell_tree { auto max = std::max_element(depth.begin(), depth.end()); auto max_leaf = std::distance(depth.begin(), max); - auto original_depth = *max; // Calculate the depth of each compartment as the maximum distance // from a child leaf diff --git a/swcio.hpp b/src/swcio.hpp similarity index 100% rename from swcio.hpp rename to src/swcio.hpp diff --git a/tree.hpp b/src/tree.hpp similarity index 98% rename from tree.hpp rename to src/tree.hpp index 772eb86c99d55ac65b076ee8ab164d1ae0c6c675..04f8ccd4d35b207a5567906d3eb84c47058340c2 100644 --- a/tree.hpp +++ b/src/tree.hpp @@ -58,10 +58,6 @@ class tree { } } - // The number of children is the number of branches, excluding the root branch. - // num_children is equivalent to the number of edges in the graph. - auto nchildren = nbranches-1; - // allocate memory for storing the tree init(nbranches); diff --git a/util.hpp b/src/util.hpp similarity index 98% rename from util.hpp rename to src/util.hpp index d37c7f65427f1bd7e12edc8ee10b0f95fcb47ba8..17a3a49b0a19ee867229f71e08ab27b2c6962d57 100644 --- a/util.hpp +++ b/src/util.hpp @@ -2,12 +2,14 @@ #include "vector/include/Vector.hpp" +/* using memory::util::red; using memory::util::yellow; using memory::util::green; using memory::util::white; using memory::util::blue; using memory::util::cyan; +*/ #include <ostream> #include <vector> diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba8b481ad51109f2a758ebd19c4281d5f3792d2f --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,27 @@ +set(HEADERS + ../src/swcio.hpp + ../src/tree.hpp + ../src/cell_tree.hpp +) + +set(TEST_SOURCES + # google test framework + gtest-all.cpp + + # unit tests + test_swcio.cpp + test_tree.cpp + + # unit test driver + main.cpp +) + +add_executable(test.exe ${TEST_SOURCES} ${HEADERS}) + +#target_link_libraries(test_compiler LINK_PUBLIC compiler gtest) + +#set_target_properties( test.exe +# PROPERTIES +# RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests" +#) + diff --git a/gtest-all.cc b/tests/gtest-all.cpp similarity index 99% rename from gtest-all.cc rename to tests/gtest-all.cpp index c3fb36ab25b692ce827eda4ccf8a85fbc3a923ae..641a64833c5b79f930e0206d82b44279f2f1dbe8 100644 --- a/gtest-all.cc +++ b/tests/gtest-all.cpp @@ -36,7 +36,7 @@ // This line ensures that gtest.h can be compiled on its own, even // when it's fused. -#include "gtest/gtest.h" +#include "gtest.h" // The following lines pull in the real gtest *.cc files. // Copyright 2005, Google Inc. diff --git a/gtest/gtest.h b/tests/gtest.h similarity index 100% rename from gtest/gtest.h rename to tests/gtest.h diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f2eab0931a0b4b2a5b17ed12ba6c2f3d1d09d27c --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,11 @@ +#include <iostream> +#include <fstream> +#include <numeric> +#include <vector> + +#include "gtest.h" + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/make_image.sh b/tests/make_image.sh similarity index 100% rename from make_image.sh rename to tests/make_image.sh diff --git a/tests/test_swcio.cpp b/tests/test_swcio.cpp new file mode 100644 index 0000000000000000000000000000000000000000..862f5a1cc343c62aa0e192e352c188bb22e86f74 --- /dev/null +++ b/tests/test_swcio.cpp @@ -0,0 +1,182 @@ +#include <iostream> +#include <fstream> +#include <numeric> +#include <vector> + +#include "gtest.h" + +#include "swcio.hpp" + +// SWC tests +void expect_cell_equals(const neuron::io::cell_record &expected, + const neuron::io::cell_record &actual) +{ + EXPECT_EQ(expected.id(), actual.id()); + EXPECT_EQ(expected.type(), actual.type()); + EXPECT_FLOAT_EQ(expected.x(), actual.x()); + EXPECT_FLOAT_EQ(expected.y(), actual.y()); + EXPECT_FLOAT_EQ(expected.z(), actual.z()); + EXPECT_FLOAT_EQ(expected.radius(), actual.radius()); + EXPECT_EQ(expected.parent(), actual.parent()); +} + +TEST(cell_record, construction) +{ + using namespace neuron::io; + + { + // force an invalid type + cell_record::kind invalid_type = static_cast<cell_record::kind>(100); + EXPECT_THROW(cell_record cell(invalid_type, 7, 1., 1., 1., 1., 5), + std::invalid_argument); + } + + { + // invalid id + EXPECT_THROW(cell_record cell( + cell_record::custom, -3, 1., 1., 1., 1., 5), + std::invalid_argument); + } + + { + // invalid parent id + EXPECT_THROW(cell_record cell( + cell_record::custom, 0, 1., 1., 1., 1., -5), + std::invalid_argument); + } + + { + // invalid radius + EXPECT_THROW(cell_record cell( + cell_record::custom, 0, 1., 1., 1., -1., -1), + std::invalid_argument); + } + + { + // parent_id > id + EXPECT_THROW(cell_record cell( + cell_record::custom, 0, 1., 1., 1., 1., 2), + std::invalid_argument); + } + + { + // parent_id == id + EXPECT_THROW(cell_record cell( + cell_record::custom, 0, 1., 1., 1., 1., 0), + std::invalid_argument); + } + + { + // check standard construction by value + cell_record cell(cell_record::custom, 0, 1., 1., 1., 1., -1); + EXPECT_EQ(cell.id(), 0); + EXPECT_EQ(cell.type(), cell_record::custom); + EXPECT_EQ(cell.x(), 1.); + EXPECT_EQ(cell.y(), 1.); + EXPECT_EQ(cell.z(), 1.); + EXPECT_EQ(cell.radius(), 1.); + EXPECT_EQ(cell.diameter(), 2*1.); + EXPECT_EQ(cell.parent(), -1); + } + + { + // check copy constructor + cell_record cell_orig(cell_record::custom, 0, 1., 1., 1., 1., -1); + cell_record cell(cell_orig); + expect_cell_equals(cell_orig, cell); + } +} + +TEST(swc_parser, invalid_input) +{ + using namespace neuron::io; + + { + // check incomplete lines; missing parent + std::istringstream is("1 1 14.566132 34.873772 7.857000 0.717830\n"); + cell_record cell; + EXPECT_THROW(is >> cell, std::logic_error); + } + + { + // Check non-parsable values + std::istringstream is("1a 1 14.566132 34.873772 7.857000 0.717830 -1\n"); + cell_record cell; + EXPECT_THROW(is >> cell, std::logic_error); + } + + { + // Check invalid cell type + std::istringstream is("1 10 14.566132 34.873772 7.857000 0.717830 -1\n"); + cell_record cell; + EXPECT_THROW(is >> cell, std::invalid_argument); + } +} + + +TEST(swc_parser, valid_input) +{ + using namespace neuron::io; + + { + // check empty file; no cell may be parsed + cell_record cell, cell_orig; + std::istringstream is(""); + EXPECT_NO_THROW(is >> cell); + expect_cell_equals(cell_orig, cell); + } + + { + // check comment-only file not ending with a newline; + // no cell may be parsed + cell_record cell, cell_orig; + std::istringstream is("#comment\n#comment"); + EXPECT_NO_THROW(is >> cell); + expect_cell_equals(cell_orig, cell); + } + + + { + // check last line case (no newline at the end) + std::istringstream is("1 1 14.566132 34.873772 7.857000 0.717830 -1"); + cell_record cell; + EXPECT_NO_THROW(is >> cell); + EXPECT_EQ(0, cell.id()); // zero-based indexing + EXPECT_EQ(cell_record::soma, cell.type()); + EXPECT_FLOAT_EQ(14.566132, cell.x()); + EXPECT_FLOAT_EQ(34.873772, cell.y()); + EXPECT_FLOAT_EQ( 7.857000, cell.z()); + EXPECT_FLOAT_EQ( 0.717830, cell.radius()); + EXPECT_FLOAT_EQ( -1, cell.parent()); + } + + { + // check valid input with a series of records + std::vector<cell_record> cells_orig = { + cell_record(cell_record::soma, 0, + 14.566132, 34.873772, 7.857000, 0.717830, -1), + cell_record(cell_record::dendrite, 1, + 14.566132+1, 34.873772+1, 7.857000+1, 0.717830+1, -1) + }; + + std::stringstream swc_input; + swc_input << "# this is a comment\n"; + swc_input << "# this is a comment\n"; + for (auto c : cells_orig) + swc_input << c << "\n"; + + swc_input << "# this is a final comment\n"; + try { + std::size_t nr_records = 0; + cell_record cell; + while ( !(swc_input >> cell).eof()) { + ASSERT_LT(nr_records, cells_orig.size()); + expect_cell_equals(cells_orig[nr_records], cell); + ++nr_records; + } + } catch (std::exception &e) { + ADD_FAILURE(); + } + } +} + diff --git a/main.cpp b/tests/test_tree.cpp similarity index 58% rename from main.cpp rename to tests/test_tree.cpp index ed8c3307699c64dc2ed19860d54de7739cd88e4e..3bd26796147d8d906fd88ae9b57c8708ef61e2e7 100644 --- a/main.cpp +++ b/tests/test_tree.cpp @@ -3,10 +3,9 @@ #include <numeric> #include <vector> -#include "gtest/gtest.h" +#include "gtest.h" #include "cell_tree.hpp" -#include "swcio.hpp" #include "json/src/json.hpp" using json = nlohmann::json; @@ -242,7 +241,7 @@ TEST(cell_tree, balance) { // from a json file and creates a .dot file for it TEST(cell_tree, json_load) { json cell_data; - std::ifstream("cells_small.json") >> cell_data; + std::ifstream("../data/cells_small.json") >> cell_data; for(auto c : range(0,cell_data.size())) { std::vector<int> parent_index = cell_data[c]["parent_index"]; @@ -252,180 +251,3 @@ TEST(cell_tree, json_load) { } } -// SWC tests -void expect_cell_equals(const neuron::io::cell_record &expected, - const neuron::io::cell_record &actual) -{ - EXPECT_EQ(expected.id(), actual.id()); - EXPECT_EQ(expected.type(), actual.type()); - EXPECT_FLOAT_EQ(expected.x(), actual.x()); - EXPECT_FLOAT_EQ(expected.y(), actual.y()); - EXPECT_FLOAT_EQ(expected.z(), actual.z()); - EXPECT_FLOAT_EQ(expected.radius(), actual.radius()); - EXPECT_EQ(expected.parent(), actual.parent()); -} - -TEST(cell_record, construction) -{ - using namespace neuron::io; - - { - // force an invalid type - cell_record::kind invalid_type = static_cast<cell_record::kind>(100); - EXPECT_THROW(cell_record cell(invalid_type, 7, 1., 1., 1., 1., 5), - std::invalid_argument); - } - - { - // invalid id - EXPECT_THROW(cell_record cell( - cell_record::custom, -3, 1., 1., 1., 1., 5), - std::invalid_argument); - } - - { - // invalid parent id - EXPECT_THROW(cell_record cell( - cell_record::custom, 0, 1., 1., 1., 1., -5), - std::invalid_argument); - } - - { - // invalid radius - EXPECT_THROW(cell_record cell( - cell_record::custom, 0, 1., 1., 1., -1., -1), - std::invalid_argument); - } - - { - // parent_id > id - EXPECT_THROW(cell_record cell( - cell_record::custom, 0, 1., 1., 1., 1., 2), - std::invalid_argument); - } - - { - // parent_id == id - EXPECT_THROW(cell_record cell( - cell_record::custom, 0, 1., 1., 1., 1., 0), - std::invalid_argument); - } - - { - // check standard construction by value - cell_record cell(cell_record::custom, 0, 1., 1., 1., 1., -1); - EXPECT_EQ(cell.id(), 0); - EXPECT_EQ(cell.type(), cell_record::custom); - EXPECT_EQ(cell.x(), 1.); - EXPECT_EQ(cell.y(), 1.); - EXPECT_EQ(cell.z(), 1.); - EXPECT_EQ(cell.radius(), 1.); - EXPECT_EQ(cell.diameter(), 2*1.); - EXPECT_EQ(cell.parent(), -1); - } - - { - // check copy constructor - cell_record cell_orig(cell_record::custom, 0, 1., 1., 1., 1., -1); - cell_record cell(cell_orig); - expect_cell_equals(cell_orig, cell); - } -} - -TEST(swc_parser, invalid_input) -{ - using namespace neuron::io; - - { - // check incomplete lines; missing parent - std::istringstream is("1 1 14.566132 34.873772 7.857000 0.717830\n"); - cell_record cell; - EXPECT_THROW(is >> cell, std::logic_error); - } - - { - // Check non-parsable values - std::istringstream is("1a 1 14.566132 34.873772 7.857000 0.717830 -1\n"); - cell_record cell; - EXPECT_THROW(is >> cell, std::logic_error); - } - - { - // Check invalid cell type - std::istringstream is("1 10 14.566132 34.873772 7.857000 0.717830 -1\n"); - cell_record cell; - EXPECT_THROW(is >> cell, std::invalid_argument); - } -} - - -TEST(swc_parser, valid_input) -{ - using namespace neuron::io; - - { - // check empty file; no cell may be parsed - cell_record cell, cell_orig; - std::istringstream is(""); - EXPECT_NO_THROW(is >> cell); - expect_cell_equals(cell_orig, cell); - } - - { - // check comment-only file not ending with a newline; - // no cell may be parsed - cell_record cell, cell_orig; - std::istringstream is("#comment\n#comment"); - EXPECT_NO_THROW(is >> cell); - expect_cell_equals(cell_orig, cell); - } - - - { - // check last line case (no newline at the end) - std::istringstream is("1 1 14.566132 34.873772 7.857000 0.717830 -1"); - cell_record cell; - EXPECT_NO_THROW(is >> cell); - EXPECT_EQ(0, cell.id()); // zero-based indexing - EXPECT_EQ(cell_record::soma, cell.type()); - EXPECT_FLOAT_EQ(14.566132, cell.x()); - EXPECT_FLOAT_EQ(34.873772, cell.y()); - EXPECT_FLOAT_EQ( 7.857000, cell.z()); - EXPECT_FLOAT_EQ( 0.717830, cell.radius()); - EXPECT_FLOAT_EQ( -1, cell.parent()); - } - - { - // check valid input with a series of records - std::vector<cell_record> cells_orig = { - cell_record(cell_record::soma, 0, - 14.566132, 34.873772, 7.857000, 0.717830, -1), - cell_record(cell_record::dendrite, 1, - 14.566132+1, 34.873772+1, 7.857000+1, 0.717830+1, -1) - }; - - std::stringstream swc_input; - swc_input << "# this is a comment\n"; - swc_input << "# this is a comment\n"; - for (auto c : cells_orig) - swc_input << c << "\n"; - - swc_input << "# this is a final comment\n"; - try { - std::size_t nr_records = 0; - cell_record cell; - while ( !(swc_input >> cell).eof()) { - ASSERT_LT(nr_records, cells_orig.size()); - expect_cell_equals(cells_orig[nr_records], cell); - ++nr_records; - } - } catch (std::exception &e) { - ADD_FAILURE(); - } - } -} - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/vector b/vector index 9c86d0a84efed0dd739888503d275378df67fe71..79ad5236b219a7618a8cda8caad6cd3de1f9e122 160000 --- a/vector +++ b/vector @@ -1 +1 @@ -Subproject commit 9c86d0a84efed0dd739888503d275378df67fe71 +Subproject commit 79ad5236b219a7618a8cda8caad6cd3de1f9e122