| # Contributing |
| This document assumes you have followed the [Apache Geode Code contribution instructions](https://cwiki.apache.org/confluence/display/GEODE/Code+contributions) |
| |
| ## Building the code |
| see [BUILDING.md](BUILDING.md) |
| |
| ## Next steps |
| * Make your changes/add your feature/fix a bug. |
| * Conform to established [versioning requirements](README.md#versioning). |
| * [Test](#testing) your feature branch changes. |
| * Check your [formatting](#clang-format) and [style](#clang-tidy). |
| * Submit a pull request. |
| |
| ## Testing |
| Before submitting a pull request all tests must pass. This includes all unit tests, integration tests, and acceptance |
| tests. We are using CTest for running tests (please see [the CTest documentation](https://cmake.org/Wiki/CMake/Testing_With_CTest) |
| for further information) and [Google Test](https://github.com/google/googletest) as testing framework. |
| |
| ### Running unit tests |
| ```console |
| $ cd build/cppcache/test/<Debug|Release|if needed> |
| $ ./apache-geode_unittests |
| ``` |
| |
| ### Running integration tests |
| There are two test suites of integration tests based on different testing frameworks. The old integration tests (stored |
| in `geode-native/cppcache/integration-test`) are based on a custom testing framework, and the newer ones (stored in |
| `geode-native/cppcache/integration/test`) are based on [Google Test](https://github.com/google/googletest). |
| |
| Old integration tests are deprecated. If your changes include the implementation of new integration test/s to be |
| verified, they should be written using Google Test. If your change implies a significant change in one or more old test |
| cases, you should create the equivalent test case/s using Google Test to substitute the old one/s instead of adapting |
| them. |
| |
| Both integration test suites can be executed using CTest. |
| |
| #### Running heritage integration test suite |
| ```console |
| $ cd build/cppcache/integration-test |
| $ ctest -C <Debug|RelWithDebInfo> --timeout 300 -j1 |
| ``` |
| Execution will take 2 hours approximately. It is possible to increase the number of jobs changing the value of `-j` |
| parameter (up to `4`) for running tests in parallel, although it may end up with failed tests that will need to be |
| re-run sequentially. |
| |
| Standalone tests can also be run as follows: |
| ```console |
| $ cd build/cppcache/integration-test |
| $ ctest -C <Debug|RelWithDebInfo> -R <test_name> |
| ``` |
| For example: |
| ```console |
| $ ctest -C RelWithDebInfo -R testCacheless |
| ``` |
| |
| .NET integration tests can be executed similarly from `build/clicache/integration-test`. |
| |
| #### Running new Google Test integration test suite |
| Make sure Docker is installed as it is required for SNI Tests |
| ```console |
| $ cd build/cppcache/integration/test |
| $ ctest -C <Debug|RelWithDebInfo> -j1 |
| ``` |
| |
| It is possible to increase the number of jobs changing the value of `-j` parameter for running tests in parallel, |
| although it may end up with failed tests that will need to be re-run sequentially. Standalone tests can also be run as |
| follows: |
| ```console |
| $ cd build/cppcache/integration/test |
| $ ctest -C <Debug|RelWithDebInfo> -R <test_name> -j1 |
| ``` |
| For example: |
| ```console |
| $ ctest -C RelWithDebInfo -R AuthInitializeTest.putGetWithBasicAuth -j1 |
| ``` |
| |
| Notice that `BasicIPv6Test` test is expected to fail due to IPv6 support is disabled by default. [BUILDING.md](BUILDING.md) |
| explains how to enable it. |
| |
| ### Running acceptance tests |
| Acceptance tests is a new category of tests that are designed to test end to end connectivity of a geode-native client |
| with a cloud based geode cluster that sits behind a proxy server. |
| |
| These tests are stored in `geode-native/cppcache/acceptance-test` and `geode-native/clicache/acceptance-test` for C++ |
| and .NET clients respectively. They utilize docker containers for the proxy server and geode servers. They are enabled |
| during cmake configuration only if docker and docker-compose are found. |
| |
| The acceptance tests can be run using ctest as follows. Note: Currently, the tests can only be run sequentially, hence |
| the -j1 flag below. |
| |
| For C++ clients: |
| ```console |
| $ cd build/cppcache/acceptance-test |
| $ ctest -C <Debug|RelWithDebInfo> -R <test_name> -j1 |
| ``` |
| For .NET clients: |
| ```console |
| $ cd build/clicache/acceptance-test |
| $ ctest -C <Debug|RelWithDebInfo> -R <test_name> -j1 |
| ``` |
| |
| ## Google Sanitizers |
| You can use the [Google Sanitizers](https://github.com/google/sanitizers), where available. These sanitizers can |
| be used to diagnose possible issues when running tests. You can enable them using combinations of the options below. |
| |
| Address Sanitizer: |
| ```console |
| $ cmake … -DUSE_SANITIZE_ADDRESS=YES … |
| ``` |
| Undefined Behavior Sanitizer: |
| ```console |
| $ cmake … -DUSE_SANITIZE_UNDEFINED=YES … |
| ``` |
| Make sanitizer warnings fatal and exit: |
| ```console |
| $ cmake … -DUSE_SANITIZE_FATAL=YES … |
| ``` |
| |
| |
| ## Style |
| |
| ### Formatting C++ |
| For C++ it is required to follow the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) and |
| have a build target that uses [clang-format](https://clang.llvm.org/docs/ClangFormat.html) to achieve compliance. |
| |
| #### Clang-format |
| |
| Individual targets in the build tree have their own dependency of the form `<<targetName>>-clangformat`, which uses |
| the `clang-format` executable, wherever it is found, to format and modified files according to the rules specfied in the |
| .clang-format file. This is helpful when submitting changes to geode-native, because an improperly formatted file will |
| fail Travis-CI and have to be fixed prior to merging any pull request. If clang-format is not installed on your system, |
| clangformat targets will not be added to your project files, and geode-native should build normally. Under some |
| circumstances, however, it may become necessary to disable `clang-format` on a system where it _is_ installed. The |
| clang-format rules are defined in the `.clang-format` files. |
| |
| To enable `clang-tidy` if CMake does not find `clang-format`: |
| |
| ```console |
| $ cmake … -DClangFormat_EXECUTABLE=/path/to/clang-format … |
| ``` |
| |
| To disable `clang-format` in the build: |
| |
| ```console |
| $ cmake … -DClangFormat_EXECUTABLE='' … |
| ``` |
| |
| On the other hand, it may also be desirable to run clang-format on the entire source tree. This is also easily done via |
| the `all-clangformat` _in a build with clang-format enabled_. If clang-format has been disabled in the cmake |
| configuration step, as above, the `all-clangformat` target will not exist, and the cmake configuration step will have to |
| be re-run with clang-format enabled. |
| |
| To run clang-format on the entire source tree: |
| |
| ```console |
| $ cmake --build . --target all-clangformat |
| ``` |
| |
| #### Clang-Tidy |
| |
| Code style and conventions are enforced with clang-tidy. The clang-tidy rules can be found in the `.clang-tidy` files. |
| |
| To enable `clang-tidy`: |
| |
| ```console |
| $ cmake … -DCMAKE_CXX_CLANG_TIDY=/path/to/clang-tidy … |
| ``` |
| |
| ### Code Cleanup |
| When editing old code please make the following changes. |
| |
| * Prefer the use of [`auto`](http://en.cppreference.com/w/cpp/language/auto) C++ or [`var`](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/var) |
| in C# where applicable. |
| ```c++ |
| std::vector<Cars> cars = lot.getCars(); |
| ``` |
| should be changed to |
| ```c++ |
| auto cars = lot.getCars(); |
| ``` |
| |
| * Prefer [range for](http://en.cppreference.com/w/cpp/language/range-for) loops over traditional for loops where |
| applicable. |
| ```c++ |
| for (std::vector<Car>::iterator i = cars.begin(); i != cars.end(), ++i) { |
| Car car = *i; |
| std::cout << car.getName(); |
| } |
| ``` |
| should be changed to |
| ```c++ |
| for (const auto& car : cars) { |
| std::cout << car.getName(); |
| } |
| ``` |
| |
| * Fix bad variable names. Variable names should be expressive. |
| ```c++ |
| double i = car.getFuelLevel(); |
| ``` |
| should be changed to |
| ```c++ |
| auto fuelLevel = car.getFuelLevel(); |
| ``` |
| |
| * Use [`override`](http://en.cppreference.com/w/cpp/language/override) on all method overrides. |
| |
| Given a base class |
| ```c++ |
| class Base { |
| public: |
| Base(); |
| virtual ~Base(); |
| virtual virtualMethod(); |
| virtual pureVirtualMethod() = 0; |
| } |
| ``` |
| the derived class |
| ```c++ |
| class Derived : public Base { |
| public: |
| virtual ~Derived(); |
| virtual virtualMethod(); |
| virtual pureVirtualMethod(); |
| } |
| ``` |
| should be changed to |
| ```c++ |
| class Derived : public Base { |
| public: |
| Derived(); |
| ~Derived() override; |
| virtualMethod() override; |
| pureVirtualMethod() override; |
| } |
| ``` |
| |
| * Fix [`std::string::c_str()`](http://en.cppreference.com/w/cpp/string/basic_string/c_str) calls where passed to |
| [`std::string`](http://en.cppreference.com/w/cpp/string/basic_string) parameters. |
| ```c++ |
| auto name = std::string("Delorean"); |
| ... |
| car.setName(name.c_str()); |
| ``` |
| should be changed to |
| ```c++ |
| auto name = std::string("Delorean"); |
| ... |
| car.setName(name); |
| ``` |
| |
| * Replace [`sprintf`](http://en.cppreference.com/w/cpp/io/c/fprintf) for logging messages with [`std::string`](http://en.cppreference.com/w/cpp/string/basic_string) |
| or [`std::stringstream`](http://en.cppreference.com/w/cpp/io/basic_stringstream). |
| ```c++ |
| char[1024] buffer; |
| sprintf(buffer, "Car::crashed: name=%s", car.getName().c_str()); |
| LOG(buffer); |
| ``` |
| should be changed to |
| ```c++ |
| LOG("Car::crashed: name=" + car.getName()); |
| ``` |
| |
| * Replace [`dynamic_cast`](http://en.cppreference.com/w/cpp/language/dynamic_cast) on [`std::shared_ptr`](http://en.cppreference.com/w/cpp/memory/shared_ptr) |
| with [`std::dynamic_pointer_cast`](http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast). Same goes for |
| [`static_cast`](http://en.cppreference.com/w/cpp/language/static_cast) to [`std::static_pointer_cast`](http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast), |
| etc. |
| ```c++ |
| std::shared_ptr<Car> car = garage.getCar(); |
| Delorean* delorean = dynamic_cast<Delorean*>(car.get()); |
| if (nullptr != delorean) { |
| delorean->setSpeed(88); |
| } |
| ``` |
| should be changed to |
| ```c++ |
| auto car = garage.getCar(); |
| if (auto delorean = std::dynamic_pointer_cast<Delorean>(car)) { |
| delorean->setSpeed(88); |
| } |
| ``` |
| |
| ## Code Coverage |
| |
| If building with GCC or Clang you can enable C++ code coverage by adding `-DUSE_CPP_COVERAGE=ON` to the CMake generator |
| command. |
| |
| ```console |
| $ cmake .. -DUSE_CPP_COVERAGE=ON … |
| ``` |
| |
| You can then generate a C++ code coverage report by downloading [lcov](http://ltp.sourceforge.net/coverage/lcov.php). |
| After acquiring lcov, finish the [Steps to build](BUILDING.md#Steps-to-build) section above. Then, run the tests as |
| described in the this document. Finally, run the following commands from the `build` directory: |
| |
| ```console |
| $ lcov --capture --directory . --output-file coverage.info |
| $ genhtml coverage.info --output-directory coverage_report |
| ``` |
| |
| You can then open the `index.html` file in the `coverage_report` directory using any browser. |