Enhance tests and improve documents about about build & install.

Should fixes #2, #4.

Signed-off-by: Tao He <linzhu.ht@alibaba-inc.com>
This commit is contained in:
Tao He 2020-10-01 01:47:43 +08:00
parent e24f3c0de9
commit 65271bb92d
7 changed files with 201 additions and 210 deletions

View File

@ -86,7 +86,7 @@ jobs:
run: | run: |
cd build cd build
/usr/local/bin/etcd & /usr/local/bin/etcd &
./bin/etcd_test CTEST_OUTPUT_ON_FAILURE=1 make test
# note: no need to clean up on CI env # note: no need to clean up on CI env

152
README.md
View File

@ -1,51 +1,38 @@
etcd-cpp-api is a C++ API for [etcd] etcd-cpp-apiv3
==============
The _etcd-cpp-apiv3_ is a C++ API for [etcd](https://etcd.io/)'s v3 client API,
i.e., `ETCDCTL_API=3`.
## Requirements ## Requirements
1. Build Boost Library(http://www.boost.org/doc/libs/1_61_0/more/getting_started/unix-variants.html)
* You can use:
```
./bootstrap.sh --with-libraries=system,thread,locale,random,chrono,regex,filesystem
./b2 install
```
* The above command will only compile those libraries indicated, thus it takes less time to build boost
2. Build Casablanca: 1. boost
* Instructions on how to build is here: https://github.com/Microsoft/cpprestsdk/wiki 2. protobuf
* For Linux environment you can omit libboost-all-dev during apt-get install, if you already have boost library installed. 3. gRPC
4. [cpprestsdk](https://github.com/microsoft/cpprestsdk), the latest version of master branch
on github should work, you can build and install this dependency using cmake with:
3. Install Protobuf: git clone https://github.com/microsoft/cpprestsdk.git
* https://github.com/google/protobuf/blob/master/src/README.md cd cpprestsdk
mkdir build && cd build
cmake .. -DCPPREST_EXCLUDE_WEBSOCKETS=ON
make -j && make install
4. Install Protoc for C++: ## Build and install
* https://github.com/grpc/grpc/blob/release-0_14/INSTALL.md
The _etcd-cpp-apiv3_ library could be easily built and installed using cmake, after all above
dependencies have been successfully installed:
mkdir build && cd build
cmake ..
make -j
## Compatible etcd version ## Compatible etcd version
Build master branch of etcd in github. As of writing, current releases of etcd does not yet support prev_value.
Only the master branch supports it.
https://github.com/coreos/etcd/blob/master/Documentation/dl_build.md (see: Build the latest version)
## Updates from etcdv2 cpp client to etcdv3 cpp client The _etcd-cpp-apiv3_ should work well with etcd > 3.0. Feel free to issue an issue to us on
See "handling directory nodes" section Github when you encounter problems when working with etcd 3.x releases.
## compiling .proto ## Usage
Proto files are stored in /proto. The proto files defined the interface of etcdv3.
You can compile it like this(if you are running this command inside /proto folder):
```
protoc -I . --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ./rpc.proto
protoc -I . --cpp_out=. ./*.proto
```
Protofiles for etcdv3 can be found here:
* https://github.com/coreos/etcd/tree/master/auth/authpb
* https://github.com/coreos/etcd/tree/master/etcdserver/etcdserverpb
* https://github.com/coreos/etcd/tree/master/mvcc/mvccpb
Note: Protofiles in the project is not sync to the protofiles in etcd master branch. If you
want to update the protofiles in this project, you need to manually update it.
## generic notes
```c++ ```c++
etcd::Client etcd("http://127.0.0.1:4001"); etcd::Client etcd("http://127.0.0.1:4001");
@ -64,7 +51,7 @@ also returns the ```etcd::Response``` object.
```c++ ```c++
etcd::Client etcd("http://127.0.0.1:4001"); etcd::Client etcd("http://127.0.0.1:4001");
pplx::task<etcd::Response> response_task = etcd.get("/test/key1").get(); pplx::task<etcd::Response> response_task = etcd.get("/test/key1");
// ... do something else // ... do something else
etcd::Response response = response_task.get(); etcd::Response response = response_task.get();
std::cout << response.value().as_string(); std::cout << response.value().as_string();
@ -113,7 +100,7 @@ this case since the respose has been already arrived (we are inside the callback
## etcd operations ## etcd operations
### reading a value ### Reading a value
You can read a value with the ```get``` method of the clinent instance. The only parameter is the You can read a value with the ```get``` method of the clinent instance. The only parameter is the
key to be read. If the read operation is successful then the value of the key can be acquired with key to be read. If the read operation is successful then the value of the key can be acquired with
@ -149,7 +136,7 @@ the ```created_index()``` and the ```modofied_index()``` methods.
} }
``` ```
### modifying a value ### Modifying a value
Setting the value of a key can be done with the ```set()``` method of the client. You simply pass Setting the value of a key can be done with the ```set()``` method of the client. You simply pass
the key and the value as string parameters and you are done. The newly set value object can be asked the key and the value as string parameters and you are done. The newly set value object can be asked
@ -192,7 +179,7 @@ some specific conditions.
the previous value equals with old_index. If the indices does not match returns with "Compare the previous value equals with old_index. If the indices does not match returns with "Compare
failed" error (code 101) failed" error (code 101)
### deleting a value ### Deleting a value
Values can be deleted with the ```rm``` method passing the key to be deleted as a parameter. The key Values can be deleted with the ```rm``` method passing the key to be deleted as a parameter. The key
should point to an existing value. There are conditional variations for deletion too. should point to an existing value. There are conditional variations for deletion too.
@ -204,18 +191,20 @@ should point to an existing value. There are conditional variations for deletion
the previous value equals with old_index. If the indices does not match returns with "Compare the previous value equals with old_index. If the indices does not match returns with "Compare
failed" error (code 101) failed" error (code 101)
### handling directory nodes ### Handling directory nodes
Directory nodes are not supported anymore in etcdv3.
However, ls and rmdir will list/delete keys defined by the prefix. mkdir method is removed since Directory nodes are not supported anymore in etcdv3. However, ls and rmdir will list/delete
etcdv3 treats everything as keys. keys defined by the prefix. mkdir method is removed since etcdv3 treats everything as keys.
1. Creating a directory: 1. Creating a directory:
Creating a directory is not supported anymore in etcdv3 cpp client. Users should remove the Creating a directory is not supported anymore in etcdv3 cpp client. Users should remove the
API from their code. API from their code.
2. Listing a directory: 2. Listing a directory:
Listing directory in etcd3 cpp client will return all keys that matched the given prefix recursively.
Listing directory in etcd3 cpp client will return all keys that matched the given prefix
recursively.
```c++ ```c++
etcd.set("/test/key1", "value1").wait(); etcd.set("/test/key1", "value1").wait();
@ -225,19 +214,25 @@ Listing directory in etcd3 cpp client will return all keys that matched the give
etcd::Response resp = etcd.ls("/test/new_dir").get(); etcd::Response resp = etcd.ls("/test/new_dir").get();
``` ```
resp.key() will have the following values:
```resp.key()``` will have the following values:
```
/test/key1 /test/key1
/test/key2 /test/key2
/test/key3 /test/key3
/test/subdir/foo /test/subdir/foo
```
Note: Regarding the returned keys when listing a directory: Note: Regarding the returned keys when listing a directory:
In etcdv3 cpp client, resp.key(0) will return "/test/new_dir/key1" since everything is treated as keys in etcdv3.
While in etcdv2 cpp client it will return "key1" and "/test/new_dir" directory should be created first before you can set "key1".
When you list a directory the response object's ```keys()``` and ```values()``` methods gives you a + In etcdv3 cpp client, resp.key(0) will return "/test/new_dir/key1" since everything is
vector of key names and values. The ```value()``` method with an integer parameter also treated as keys in etcdv3.
+ While in etcdv2 cpp client it will return "key1" and "/test/new_dir" directory should
be created first before you can set "key1".
When you list a directory the response object's ```keys()``` and ```values()``` methods gives
you a vector of key names and values. The ```value()``` method with an integer parameter also
returns with the i-th element of the values vector, so ```response.values()[i] == returns with the i-th element of the values vector, so ```response.values()[i] ==
response.value(i)```. response.value(i)```.
@ -252,9 +247,11 @@ response.value(i)```.
``` ```
3. Removing directory: 3. Removing directory:
If you want the delete recursively then you have to pass a second ```true``` parameter If you want the delete recursively then you have to pass a second ```true``` parameter
to rmdir and supply a key. This key will be treated as a prefix. All keys that match the prefix will to rmdir and supply a key. This key will be treated as a prefix. All keys that match the
be deleted. All deleted keys will be placed in response.values() and response.keys(). This parameter defaults to ```false```. prefix will be deleted. All deleted keys will be placed in ```response.values()``` and
```response.keys()```. This parameter defaults to ```false```.
```c++ ```c++
etcd::Client etcd("http://127.0.0.1:4001"); etcd::Client etcd("http://127.0.0.1:4001");
@ -267,12 +264,12 @@ be deleted. All deleted keys will be placed in response.values() and response.ke
std::cout << resp.keys(i); std::cout << resp.keys(i);
std::cout << " = " << resp.value(i).as_string() << std::endl; std::cout << " = " << resp.value(i).as_string() << std::endl;
} }
``` ```
However, if recursive parameter is false, functionality will be the same as just deleting a key. However, if recursive parameter is false, functionality will be the same as just deleting a key.
The key supplied will NOT be treated as a prefix and will be treated as a normal key name. The key supplied will NOT be treated as a prefix and will be treated as a normal key name.
### watching for changes ### Watching for changes
Watching for a change is possible with the ```watch()``` operation of the client. The watch method Watching for a change is possible with the ```watch()``` operation of the client. The watch method
simply does not deliver a response object until the watched value changes in any way (modified or simply does not deliver a response object until the watched value changes in any way (modified or
@ -283,12 +280,15 @@ value and ```response.prev_value()``` will contain the previous value. In case o
```response.action()``` will return "delete", ```response.value()``` will be empty and should not be ```response.action()``` will return "delete", ```response.value()``` will be empty and should not be
called at all and ```response.prev_value()``` will contain the deleted value. called at all and ```response.prev_value()``` will contain the deleted value.
As mentioned in the section "handling directory nodes", directory nodes are not supported anymore in etcdv3. As mentioned in the section "handling directory nodes", directory nodes are not supported anymore
However it is still possible to watch a whole "directory subtree", or more specifically a set of keys that match the in etcdv3.
prefix, for changes with passing ```true``` to the second ```recursive``` parameter of ```watch```
(this parameter defaults to ```false``` if omitted). In this case the modified value object's ```key()``` method can be However it is still possible to watch a whole "directory subtree", or more specifically a set of
handy to determine what key is actually changed. Since this can be a long lasting operation you have to be prepared that is keys that match the prefix, for changes with passing ```true``` to the second ```recursive```
terminated by an exception and you have to restart the watch operation. parameter of ```watch``` (this parameter defaults to ```false``` if omitted). In this case the
modified value object's ```key()``` method can be handy to determine what key is actually changed.
Since this can be a long lasting operation you have to be prepared that is terminated by an
exception and you have to restart the watch operation.
The watch also accepts an index parameter that specifies what is the first change we are interested The watch also accepts an index parameter that specifies what is the first change we are interested
about. Since etcd stores the last couple of modifications with this feature you can ensure that your about. Since etcd stores the last couple of modifications with this feature you can ensure that your
@ -319,39 +319,43 @@ callback is executed by some thread from the pplx library's thread pool and the
case a small lambda function actually) will call ```watch_for_changes``` again from there. case a small lambda function actually) will call ```watch_for_changes``` again from there.
### requesting for lease ### Requesting for lease
Users can request for lease which is governed by a time-to-live(TTL) value given by the user. Users can request for lease which is governed by a time-to-live(TTL) value given by the user.
Moreover, user can attached the lease to a key(s) by indicating the lease id in add(), set(), modify() and modify_if(). Moreover, user can attached the lease to a key(s) by indicating the lease id in ```add()```,
Also the ttl will that was granted by etcd server will be indicated in ttl(). ```set()```, ```modify()``` and ```modify_if()```. Also the ttl will that was granted by etcd
server will be indicated in ```ttl()```.
```c++ ```c++
etcd::Client etcd("http://127.0.0.1:4001"); etcd::Client etcd("http://127.0.0.1:4001");
etcd::Response resp = etcd.leasegrant(60).get(); etcd::Response resp = etcd.leasegrant(60).get();
etcd.set("/test/key2", "bar", resp.value().lease()); etcd.set("/test/key2", "bar", resp.value().lease());
std::cout <<"ttl" << resp.value().ttl(); std::cout <<"ttl" << resp.value().ttl();
``` ```
### Watcher Class ### Watcher Class
Users can watch a key indefinitely or until user cancels the watch. This can be done by instantiating a Watcher class.
The supplied callback function in Watcher class will be called every time there is an event for the specified key. Users can watch a key indefinitely or until user cancels the watch. This can be done by
Watch stream will be cancelled either by user implicitly calling Cancel() or when watcher class is destroyed. instantiating a Watcher class. The supplied callback function in Watcher class will be
called every time there is an event for the specified key. Watch stream will be cancelled
either by user implicitly calling ```Cancel()``` or when watcher class is destroyed.
```c++ ```c++
etcd::Watcher watcher("http://127.0.0.1:2379", "/test", printResponse); etcd::Watcher watcher("http://127.0.0.1:2379", "/test", printResponse);
etcd.set("/test/key", "42"); /* print response will be called */ etcd.set("/test/key", "42"); /* print response will be called */
etcd.set("/test/key", "43"); /* print response will be called */ etcd.set("/test/key", "43"); /* print response will be called */
watcher.Cancel(); watcher.Cancel();
etcd.set("/test/key", "43"); /* print response will NOT be called, since watch is already cancelled */ etcd.set("/test/key", "43"); /* print response will NOT be called,
since watch is already cancelled */
} }
``` ```
### TODO ### TODO
1. Cancellation of asynchronous calls(except for watch) 1. Cancellation of asynchronous calls(except for watch)
2. LeaseKeepAlive 2. LeaseKeepAlive
3. Authentication
## License ## License
This project is licensed under the BSD-3-Clause license - see the [LICENSE](https://github.com/nokia/etcd-cpp-apiv3/blob/master/LICENSE.txt). This project is licensed under the BSD-3-Clause license - see the [LICENSE](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/blob/master/LICENSE.txt).

View File

@ -1,16 +1,22 @@
find_path(CATCH_INCLUDE_DIR NAMES catch.hpp PATHS ${PROJECT_SOURCE_DIR}) find_path(CATCH_INCLUDE_DIR NAMES catch.hpp PATHS ${PROJECT_SOURCE_DIR})
include_directories(${CATCH_INCLUDE_DIR}) include_directories(${CATCH_INCLUDE_DIR})
add_executable(etcd_test add_custom_target(check ${CMAKE_COMMAND} -E env CTEST_OUTPUT_ON_FAILURE=1
EtcdTest.cpp ${CMAKE_CTEST_COMMAND} -C $<CONFIG> --verbose
EtcdSyncTest.cpp WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
WatcherTest.cpp
LockTest.cpp
)
set_property(TARGET etcd_test PROPERTY CXX_STANDARD 11)
target_include_directories(etcd_test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen)
target_include_directories(etcd_test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen/proto)
target_link_libraries(etcd_test etcd-cpp-api) file(GLOB TEST_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
foreach(testfile ${TEST_FILES})
string(REGEX MATCH "^(.*)\\.[^.]*$" dummy ${testfile})
set(test_name ${CMAKE_MATCH_1})
message(STATUS "Found unit_test - " ${test_name})
add_executable(${test_name} ${CMAKE_CURRENT_SOURCE_DIR}/${testfile})
add_test(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>)
add_test(etcd_test etcd_test) set_property(TARGET ${test_name} PROPERTY CXX_STANDARD 11)
target_include_directories(${test_name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen)
target_include_directories(${test_name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen/proto)
target_link_libraries(${test_name} etcd-cpp-api)
add_dependencies(check ${test_name})
endforeach()

View File

@ -1,3 +1,4 @@
#define CATCH_CONFIG_MAIN
#include <catch.hpp> #include <catch.hpp>
#include "etcd/SyncClient.hpp" #include "etcd/SyncClient.hpp"
@ -24,25 +25,26 @@ TEST_CASE("sync operations")
CHECK(0 == etcd.set("/test/key2", "43").error_code()); // create new CHECK(0 == etcd.set("/test/key2", "43").error_code()); // create new
CHECK("43" == etcd.set("/test/key2", "44").prev_value().as_string()); CHECK("43" == etcd.set("/test/key2", "44").prev_value().as_string());
CHECK("" == etcd.set("/test/key3", "44").prev_value().as_string()); CHECK("" == etcd.set("/test/key3", "44").prev_value().as_string());
//CHECK(102 == etcd.set("/test", "42").error_code()); // Not a file
// get
CHECK("43" == etcd.get("/test/key1").value().as_string());
CHECK("44" == etcd.get("/test/key2").value().as_string());
CHECK("44" == etcd.get("/test/key3").value().as_string());
CHECK(100 == etcd.get("/test/key4").error_code()); // key not found
// rm // rm
CHECK(3 == etcd.ls("/test").keys().size()); CHECK(3 == etcd.ls("/test").keys().size());
CHECK(0 == etcd.rm("/test/key1").error_code()); CHECK(0 == etcd.rm("/test/key1").error_code());
CHECK(2 == etcd.ls("/test").keys().size()); CHECK(2 == etcd.ls("/test").keys().size());
// mkdir
//CHECK(etcd.mkdir("/test/new_dir").value().is_dir());
// ls // ls
CHECK(0 == etcd.ls("/test/new_dir").keys().size()); CHECK(0 == etcd.ls("/test/new_dir").keys().size());
etcd.set("/test/new_dir/key1", "value1"); etcd.set("/test/new_dir/key1", "value1");
etcd.set("/test/new_dir/key2", "value2"); etcd.set("/test/new_dir/key2", "value2");
//etcd.mkdir("/test/new_dir/sub_dir");
CHECK(2 == etcd.ls("/test/new_dir").keys().size()); CHECK(2 == etcd.ls("/test/new_dir").keys().size());
// rmdir // rmdir
//CHECK(108 == etcd.rmdir("/test/new_dir").error_code()); // Directory not empty CHECK(100 == etcd.rmdir("/test/new_dir").error_code()); // key not found
CHECK(0 == etcd.rmdir("/test/new_dir", true).error_code()); CHECK(0 == etcd.rmdir("/test/new_dir", true).error_code());
// compare and swap // compare and swap
@ -102,64 +104,74 @@ TEST_CASE("sync operations")
CHECK(leaseid == res.value().lease()); CHECK(leaseid == res.value().lease());
CHECK("44" == res.value().as_string()); CHECK("44" == res.value().as_string());
// TEST_CASE("wait for a value change") REQUIRE(0 == etcd.rmdir("/test", true).error_code());
// { }
// etcd::Client etcd(etcd_uri);
// etcd.set("/test/key1", "42").wait();
// pplx::task<etcd::Response> res = etcd.watch("/test/key1"); TEST_CASE("wait for a value change")
// CHECK(!res.is_done()); {
etcd::Client etcd(etcd_uri);
etcd.set("/test/key1", "42").wait();
// etcd.set("/test/key1", "43").get(); pplx::task<etcd::Response> res = etcd.watch("/test/key1");
// sleep(1); CHECK(!res.is_done());
// REQUIRE(res.is_done()); etcd.set("/test/key1", "43").get();
// REQUIRE("set" == res.get().action()); sleep(1);
// CHECK("43" == res.get().value().as_string());
// }
// TEST_CASE("wait for a directory change") REQUIRE(res.is_done());
// { REQUIRE("set" == res.get().action());
// etcd::Client etcd(etcd_uri); CHECK("43" == res.get().value().as_string());
// pplx::task<etcd::Response> res = etcd.watch("/test", true); REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
}
// etcd.add("/test/key4", "44").wait(); TEST_CASE("wait for a directory change")
// REQUIRE(res.is_done()); {
// CHECK("create" == res.get().action()); etcd::Client etcd(etcd_uri);
// CHECK("44" == res.get().value().as_string());
// pplx::task<etcd::Response> res2 = etcd.watch("/test", true); pplx::task<etcd::Response> res = etcd.watch("/test", true);
// etcd.set("/test/key4", "45").wait(); etcd.add("/test/key4", "44").wait();
// sleep(1); sleep(1);
// REQUIRE(res2.is_done()); REQUIRE(res.is_done());
// CHECK("set" == res2.get().action()); CHECK("create" == res.get().action());
// CHECK("45" == res2.get().value().as_string()); CHECK("44" == res.get().value().as_string());
// }
// TEST_CASE("watch changes in the past") pplx::task<etcd::Response> res2 = etcd.watch("/test", true);
// {
// etcd::Client etcd(etcd_uri);
// int index = etcd.set("/test/key1", "42").get().index(); etcd.set("/test/key4", "45").wait();
sleep(1);
REQUIRE(res2.is_done());
CHECK("set" == res2.get().action());
CHECK("45" == res2.get().value().as_string());
// etcd.set("/test/key1", "43").wait(); REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
// etcd.set("/test/key1", "44").wait(); }
// etcd.set("/test/key1", "45").wait();
// etcd::Response res = etcd.watch("/test/key1", ++index).get(); TEST_CASE("watch changes in the past")
// CHECK("set" == res.action()); {
// CHECK("43" == res.value().as_string()); etcd::Client etcd(etcd_uri);
// res = etcd.watch("/test/key1", ++index).get(); int index = etcd.set("/test/key1", "42").get().index();
// CHECK("set" == res.action());
// CHECK("44" == res.value().as_string());
// res = etcd.watch("/test", ++index, true).get(); etcd.set("/test/key1", "43").wait();
// CHECK("set" == res.action()); etcd.set("/test/key1", "44").wait();
// CHECK("45" == res.value().as_string()); etcd.set("/test/key1", "45").wait();
// }
etcd::Response res = etcd.watch("/test/key1", ++index).get();
CHECK("set" == res.action());
CHECK("43" == res.value().as_string());
res = etcd.watch("/test/key1", ++index).get();
CHECK("set" == res.action());
CHECK("44" == res.value().as_string());
res = etcd.watch("/test", ++index, true).get();
CHECK("set" == res.action());
CHECK("45" == res.value().as_string());
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
}
// TEST_CASE("request cancellation") // TEST_CASE("request cancellation")
// { // {
@ -173,19 +185,13 @@ TEST_CASE("sync operations")
// sleep(1); // sleep(1);
// REQUIRE(res.is_done()); // REQUIRE(res.is_done());
// try // try {
// {
// res.wait(); // res.wait();
// } // }
// catch(pplx::task_canceled const & ex) // catch(pplx::task_canceled const & ex) {
// {
// std::cout << "pplx::task_canceled: " << ex.what() << "\n"; // std::cout << "pplx::task_canceled: " << ex.what() << "\n";
// } // }
// catch(std::exception const & ex) // catch(std::exception const & ex) {
// {
// std::cout << "std::exception: " << ex.what() << "\n"; // std::cout << "std::exception: " << ex.what() << "\n";
// } // }
// } // }
REQUIRE(0 == etcd.rmdir("/test", true).error_code());
}

View File

@ -1,5 +1,6 @@
#define CATCH_CONFIG_MAIN #define CATCH_CONFIG_MAIN
#include <catch.hpp> #include <catch.hpp>
#include <iostream> #include <iostream>
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
@ -11,10 +12,8 @@ TEST_CASE("setup")
etcd.rmdir("/test", true).wait(); etcd.rmdir("/test", true).wait();
} }
TEST_CASE("add a new key") TEST_CASE("add a new key")
{ {
etcd::Client etcd("http://127.0.0.1:2379"); etcd::Client etcd("http://127.0.0.1:2379");
etcd.rmdir("/test", true).wait(); etcd.rmdir("/test", true).wait();
etcd::Response resp = etcd.add("/test/key1", "42").get(); etcd::Response resp = etcd.add("/test/key1", "42").get();
@ -32,7 +31,6 @@ TEST_CASE("add a new key")
CHECK("Key already exists" == etcd.add("/test/key1", "42").get().error_message()); CHECK("Key already exists" == etcd.add("/test/key1", "42").get().error_message());
} }
TEST_CASE("read a value from etcd") TEST_CASE("read a value from etcd")
{ {
etcd::Client etcd("http://127.0.0.1:2379"); etcd::Client etcd("http://127.0.0.1:2379");
@ -41,11 +39,9 @@ TEST_CASE("read a value from etcd")
REQUIRE(resp.is_ok()); REQUIRE(resp.is_ok());
REQUIRE(0 == resp.error_code()); REQUIRE(0 == resp.error_code());
CHECK("42" == resp.value().as_string()); CHECK("42" == resp.value().as_string());
CHECK("" == etcd.get("/test").get().value().as_string()); // key points to a directory CHECK("" == etcd.get("/test").get().value().as_string()); // key points to a directory
} }
TEST_CASE("simplified read") TEST_CASE("simplified read")
{ {
etcd::Client etcd("http://127.0.0.1:2379"); etcd::Client etcd("http://127.0.0.1:2379");
@ -54,8 +50,6 @@ TEST_CASE("simplified read")
CHECK("" == etcd.get("/test/key2").get().value().as_string()); // Key not found CHECK("" == etcd.get("/test/key2").get().value().as_string()); // Key not found
} }
TEST_CASE("modify a key") TEST_CASE("modify a key")
{ {
etcd::Client etcd("http://127.0.0.1:2379"); etcd::Client etcd("http://127.0.0.1:2379");
@ -66,7 +60,6 @@ TEST_CASE("modify a key")
CHECK("43" == etcd.modify("/test/key1", "42").get().prev_value().as_string()); CHECK("43" == etcd.modify("/test/key1", "42").get().prev_value().as_string());
} }
TEST_CASE("set a key") TEST_CASE("set a key")
{ {
etcd::Client etcd("http://127.0.0.1:2379"); etcd::Client etcd("http://127.0.0.1:2379");
@ -110,10 +103,8 @@ TEST_CASE("atomic compare-and-swap")
CHECK(!res.is_ok()); CHECK(!res.is_ok());
CHECK(100 == res.error_code()); CHECK(100 == res.error_code());
CHECK("Key not found" == res.error_message()); CHECK("Key not found" == res.error_message());
} }
TEST_CASE("delete a value") TEST_CASE("delete a value")
{ {
etcd::Client etcd("http://127.0.0.1:2379"); etcd::Client etcd("http://127.0.0.1:2379");
@ -188,7 +179,6 @@ TEST_CASE("deep atomic compare-and-swap")
CHECK(101 == res.error_code()); CHECK(101 == res.error_code());
CHECK("Compare failed" == res.error_message()); CHECK("Compare failed" == res.error_message());
// succes with the correct index // succes with the correct index
res = etcd.modify_if("/test/key1", "44", index).get(); res = etcd.modify_if("/test/key1", "44", index).get();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -200,21 +190,8 @@ TEST_CASE("deep atomic compare-and-swap")
CHECK(!res.is_ok()); CHECK(!res.is_ok());
CHECK(101 == res.error_code()); CHECK(101 == res.error_code());
CHECK("Compare failed" == res.error_message()); CHECK("Compare failed" == res.error_message());
} }
//skip this test case
/*
TEST_CASE("create a directory")
{
etcd::Client etcd("http://127.0.0.1:4001");
etcd::Response resp = etcd.mkdir("/test/new_dir").get();
CHECK("set" == resp.action());
CHECK(resp.value().is_dir());
}
*/
TEST_CASE("list a directory") TEST_CASE("list a directory")
{ {
etcd::Client etcd("http://127.0.0.1:2379"); etcd::Client etcd("http://127.0.0.1:2379");
@ -249,7 +226,7 @@ TEST_CASE("delete a directory")
{ {
etcd::Client etcd("http://127.0.0.1:2379"); etcd::Client etcd("http://127.0.0.1:2379");
//CHECK(108 == etcd.rmdir("/test/new_dir").get().error_code()); // Directory not empty CHECK(100 == etcd.rmdir("/test/new_dir").get().error_code()); // key not found
etcd::Response resp = etcd.ls("/test/new_dir").get(); etcd::Response resp = etcd.ls("/test/new_dir").get();

View File

@ -1,5 +1,7 @@
#include <atomic> #define CATCH_CONFIG_MAIN
#include <catch.hpp> #include <catch.hpp>
#include <atomic>
#include <iostream> #include <iostream>
#include <thread> #include <thread>
@ -23,7 +25,6 @@ TEST_CASE("lock and unlock")
REQUIRE(0 == resp2.error_code()); REQUIRE(0 == resp2.error_code());
} }
TEST_CASE("double lock will fail") TEST_CASE("double lock will fail")
{ {
etcd::Client etcd("http://127.0.0.1:2379"); etcd::Client etcd("http://127.0.0.1:2379");

View File

@ -1,3 +1,4 @@
#define CATCH_CONFIG_MAIN
#include <catch.hpp> #include <catch.hpp>
#include "etcd/Watcher.hpp" #include "etcd/Watcher.hpp"
@ -10,8 +11,9 @@ void printResponse(etcd::Response const & resp)
{ {
++watcher_called; ++watcher_called;
std::cout << "print response called" << std::endl; std::cout << "print response called" << std::endl;
if (resp.error_code()) if (resp.error_code()) {
std::cout << resp.error_code() << ": " << resp.error_message() << std::endl; std::cout << resp.error_code() << ": " << resp.error_message() << std::endl;
}
else else
{ {
std::cout << resp.action() << " " << resp.value().as_string() << std::endl; std::cout << resp.action() << " " << resp.value().as_string() << std::endl;
@ -46,7 +48,6 @@ TEST_CASE("create watcher with cancel")
TEST_CASE("create watcher") TEST_CASE("create watcher")
{ {
etcd::SyncClient etcd(etcd_uri); etcd::SyncClient etcd(etcd_uri);
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
@ -74,17 +75,13 @@ TEST_CASE("create watcher")
// sleep(1); // sleep(1);
// REQUIRE(res.is_done()); // REQUIRE(res.is_done());
// try // try {
// {
// res.wait(); // res.wait();
// } // }
// catch(pplx::task_canceled const & ex) // catch(pplx::task_canceled const & ex) {
// {
// std::cout << "pplx::task_canceled: " << ex.what() << "\n"; // std::cout << "pplx::task_canceled: " << ex.what() << "\n";
// } // }
// catch(std::exception const & ex) // catch(std::exception const & ex) {
// {
// std::cout << "std::exception: " << ex.what() << "\n"; // std::cout << "std::exception: " << ex.what() << "\n";
// } // }
// } // }