blob: 669d6e8aa77ba03c7c2e6bd0e4381b010df04a03 [file] [log] [blame] [view]
# 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.