Skip to content

Commit

Permalink
wifi(vts): simplify HIDL calls
Browse files Browse the repository at this point in the history
Presently, the core of the Wifi test logic is
obscured by the boilerplate required to create
a callback.

This CL provides some utilities to simplify
the creation of a HIDL result callback, and
migrates existing Wifi code to use the new
utilities.

Along the way: add a .clang-format file, so
that I don't misformat code with 2-space
indents (the Google default).

Bug: 34817351
Test: vts-tradefed run commandAndExit vts --module=HalWifiHidlTargetTest
Change-Id: Id2c728f96c3369c74adc8dfce7228b0a15a0781e
  • Loading branch information
gquiche committed Feb 15, 2017
1 parent 751dc69 commit 90f3217
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 103 deletions.
2 changes: 2 additions & 0 deletions wifi/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
BasedOnStyle: Google
IndentWidth: 4
1 change: 1 addition & 0 deletions wifi/1.0/vts/functional/Android.bp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ cc_test {
"main.cpp",
"wifi_ap_iface_hidl_test.cpp",
"wifi_chip_hidl_test.cpp",
"wifi_hidl_call_util_selftest.cpp",
"wifi_hidl_test.cpp",
"wifi_hidl_test_utils.cpp",
"wifi_nan_iface_hidl_test.cpp",
Expand Down
123 changes: 123 additions & 0 deletions wifi/1.0/vts/functional/wifi_hidl_call_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>

#include <gtest/gtest.h>

namespace {
namespace detail {
template <typename>
struct functionArgSaver;

// Provides a std::function that takes one argument, and a buffer
// wherein the function will store its argument. The buffer has
// the same type as the argument, but with const and reference
// modifiers removed.
template <typename ArgT>
struct functionArgSaver<std::function<void(ArgT)>> final {
using StorageT = typename std::remove_const<
typename std::remove_reference<ArgT>::type>::type;

std::function<void(ArgT)> saveArgs = [this](ArgT arg) {
this->saved_values = arg;
};

StorageT saved_values;
};

// Provides a std::function that takes two arguments, and a buffer
// wherein the function will store its arguments. The buffer is a
// std::pair, whose elements have the same types as the arguments
// (but with const and reference modifiers removed).
template <typename Arg1T, typename Arg2T>
struct functionArgSaver<std::function<void(Arg1T, Arg2T)>> final {
using StorageT =
std::pair<typename std::remove_const<
typename std::remove_reference<Arg1T>::type>::type,
typename std::remove_const<
typename std::remove_reference<Arg2T>::type>::type>;

std::function<void(Arg1T, Arg2T)> saveArgs = [this](Arg1T arg1,
Arg2T arg2) {
this->saved_values = {arg1, arg2};
};

StorageT saved_values;
};

// Provides a std::function that takes three or more arguments, and a
// buffer wherein the function will store its arguments. The buffer is a
// std::tuple whose elements have the same types as the arguments (but
// with const and reference modifiers removed).
template <typename... ArgT>
struct functionArgSaver<std::function<void(ArgT...)>> final {
using StorageT = std::tuple<typename std::remove_const<
typename std::remove_reference<ArgT>::type>::type...>;

std::function<void(ArgT...)> saveArgs = [this](ArgT... arg) {
this->saved_values = {arg...};
};

StorageT saved_values;
};

// Invokes |method| on |object|, providing |method| a CallbackT as the
// final argument. Returns a copy of the parameters that |method| provided
// to CallbackT. (The parameters are returned by value.)
template <typename CallbackT, typename MethodT, typename ObjectT,
typename... ArgT>
typename functionArgSaver<CallbackT>::StorageT invokeMethod(
MethodT method, ObjectT object, ArgT&&... methodArg) {
functionArgSaver<CallbackT> result_buffer;
const auto& res = ((*object).*method)(std::forward<ArgT>(methodArg)...,
result_buffer.saveArgs);
EXPECT_TRUE(res.isOk());
return result_buffer.saved_values;
}
} // namespace detail
} // namespace

// Invokes |method| on |strong_pointer|, passing provided arguments through to
// |method|.
//
// Returns either:
// - A copy of the result callback parameter (for callbacks with a single
// parameter), OR
// - A pair containing a copy of the result callback parameters (for callbacks
// with two parameters), OR
// - A tuple containing a copy of the result callback paramters (for callbacks
// with three or more parameters).
//
// Example usage:
// EXPECT_EQ(WifiStatusCode::SUCCESS,
// HIDL_INVOKE(strong_pointer, methodReturningWifiStatus).code);
// EXPECT_EQ(WifiStatusCode::SUCCESS,
// HIDL_INVOKE(strong_pointer, methodReturningWifiStatusAndOneMore)
// .first.code);
// EXPECT_EQ(WifiStatusCode::SUCCESS, std::get<0>(
// HIDL_INVOKE(strong_pointer, methodReturningWifiStatusAndTwoMore))
// .code);
#define HIDL_INVOKE(strong_pointer, method, ...) \
(detail::invokeMethod< \
std::remove_reference<decltype(*strong_pointer)>::type::method##_cb>( \
&std::remove_reference<decltype(*strong_pointer)>::type::method, \
strong_pointer, ##__VA_ARGS__))
114 changes: 114 additions & 0 deletions wifi/1.0/vts/functional/wifi_hidl_call_util_selftest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <functional>
#include <type_traits>

#include <hidl/Status.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>

#include "wifi_hidl_call_util.h"

namespace {
/*
* Example of a user-defined data-type.
*
* Used to verify that, within the internals of HIDL_INVOKE,
* reference parameters are stored by copy.
*/
class Dummy {};

/*
* Example of what a HIDL-generated proxy might look like.
*/
class IExample : public ::android::RefBase {
public:
// The callback type, for a method called startWithCallbackCopy, which
// has a callback that takes an |int|. Both the name, and the value,
// must match what would appear in HIDL-generated code.
using startWithCallbackCopy_cb = std::function<void(int)>;

// The callback type, for a method called startWithCallbackReference, which
// has a callback that takes an |int|. Both the name, and the value,
// must match what would appear in HIDL-generated code.
using startWithCallbackReference_cb = std::function<void(int)>;

// Constants which allow tests to verify that the proxy methods can
// correctly return a value. We use different values for by-copy and
// by-reference, to double-check that a call was dispatched properly.
static constexpr int kByCopyResult = 42;
static constexpr int kByReferenceResult = 420;

// Example of what a no-arg method would look like, if the callback
// is passed by-value.
::android::hardware::Return<void> startWithCallbackCopy(
startWithCallbackCopy_cb _hidl_cb) {
_hidl_cb(kByCopyResult);
return ::android::hardware::Void();
}
// Example of what a no-arg method would look like, if the callback
// is passed by const-reference.
::android::hardware::Return<void> startWithCallbackReference(
const startWithCallbackReference_cb& _hidl_cb) {
_hidl_cb(kByReferenceResult);
return ::android::hardware::Void();
}
};

constexpr int IExample::kByCopyResult;
constexpr int IExample::kByReferenceResult;
} // namespace

static_assert(std::is_same<int, detail::functionArgSaver<
std::function<void(int)>>::StorageT>::value,
"Single-arg result should be stored directly.");

static_assert(
std::is_same<std::pair<int, long>, detail::functionArgSaver<std::function<
void(int, long)>>::StorageT>::value,
"Two-arg result should be stored as a pair.");

static_assert(
std::is_same<std::tuple<char, int, long>,
detail::functionArgSaver<
std::function<void(char, int, long)>>::StorageT>::value,
"Three-arg result should be stored as a tuple.");

static_assert(std::is_same<Dummy, detail::functionArgSaver<std::function<
void(const Dummy&)>>::StorageT>::value,
"Reference should be stored by copy.");

/*
* Verifies that HIDL_INVOKE can be used with methods that take the result
* callback as a by-value parameter. (This reflects the current implementation
* of HIDL-generated code.)
*/
TEST(HidlInvokeTest, WorksWithMethodThatTakesResultCallbackByValue) {
::android::sp<IExample> sp = new IExample();
EXPECT_EQ(IExample::kByCopyResult, HIDL_INVOKE(sp, startWithCallbackCopy));
}

/*
* Verifies that HIDL_INVOKE can be used with methods that take the result
* callback as a const-reference parameter. (This ensures that HIDL_INVOKE will
* continue to work, if the HIDL-generated code switches to const-ref.)
*/
TEST(HidlInvokeTest, WorksWithMethodThatTakesResultCallbackByConstReference) {
::android::sp<IExample> sp = new IExample();
EXPECT_EQ(IExample::kByReferenceResult,
HIDL_INVOKE(sp, startWithCallbackReference));
}
Loading

0 comments on commit 90f3217

Please sign in to comment.