Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2ba59c5b authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Move result.h from init to libbase"

parents 44cabca1 8fd64c8a
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -141,6 +141,7 @@ cc_test {
        "parsenetaddress_test.cpp",
        "parsenetaddress_test.cpp",
        "properties_test.cpp",
        "properties_test.cpp",
        "quick_exit_test.cpp",
        "quick_exit_test.cpp",
        "result_test.cpp",
        "scopeguard_test.cpp",
        "scopeguard_test.cpp",
        "stringprintf_test.cpp",
        "stringprintf_test.cpp",
        "strings_test.cpp",
        "strings_test.cpp",
+164 −0
Original line number Original line Diff line number Diff line
/*
 * 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.
 */

// This file contains classes for returning a successful result along with an optional
// arbitrarily typed return value or for returning a failure result along with an optional string
// indicating why the function failed.

// There are 3 classes that implement this functionality and one additional helper type.
//
// Result<T> either contains a member of type T that can be accessed using similar semantics as
// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
// Result<T>::error().
//
// ResultError is a type that contains both a std::string describing the error and a copy of errno
// from when the error occurred.  ResultError can be used in an ostream directly to print its
// string value.
//
// Success is a typedef that aids in creating Result<T> that do not contain a return value.
// Result<Success> is the correct return type for a function that either returns successfully or
// returns an error value.  Returning Success() from a function that returns Result<Success> is the
// correct way to indicate that a function without a return type has completed successfully.
//
// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
// to T or from the constructor arguments for T.  This allows you to return a type T directly from
// a function that returns Result<T>.
//
// Error and ErrnoError are used to construct a Result<T> that has failed.  The Error class takes
// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
// to the end of the failure string to aid in interacting with C APIs.  Alternatively, an errno
// value can be directly specified via the Error() constructor.
//
// ResultError can be used in the ostream when using Error to construct a Result<T>.  In this case,
// the string that the ResultError takes is passed through the stream normally, but the errno is
// passed to the Result<T>.  This can be used to pass errno from a failing C function up multiple
// callers.
//
// ResultError can also directly construct a Result<T>.  This is particularly useful if you have a
// function that return Result<T> but you have a Result<U> and want to return its error.  In this
// case, you can return the .error() from the Result<U> to construct the Result<T>.

// An example of how to use these is below:
// Result<U> CalculateResult(const T& input) {
//   U output;
//   if (!SomeOtherCppFunction(input, &output)) {
//     return Error() << "SomeOtherCppFunction(" << input << ") failed";
//   }
//   if (!c_api_function(output)) {
//     return ErrnoError() << "c_api_function(" << output << ") failed";
//   }
//   return output;
// }
//
// auto output = CalculateResult(input);
// if (!output) return Error() << "CalculateResult failed: " << output.error();
// UseOutput(*output);

#pragma once

#include <errno.h>

#include <sstream>
#include <string>

#include "android-base/expected.h"

namespace android {
namespace base {

struct ResultError {
  template <typename T>
  ResultError(T&& message, int code)
      : message_(std::forward<T>(message)), code_(code) {}

  template <typename T>
  operator android::base::expected<T, ResultError>() {
    return android::base::unexpected(ResultError(message_, code_));
  }

  std::string message() const { return message_; }
  int code() const { return code_; }

 private:
  std::string message_;
  int code_;
};

inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
  os << t.message();
  return os;
}

class Error {
 public:
  Error() : errno_(0), append_errno_(false) {}
  Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}

  template <typename T>
  operator android::base::expected<T, ResultError>() {
    return android::base::unexpected(ResultError(str(), errno_));
  }

  template <typename T>
  Error& operator<<(T&& t) {
    int saved = errno;
    ss_ << t;
    errno = saved;
    return *this;
  }

  Error& operator<<(const ResultError& result_error) {
    (*this) << result_error.message();
    errno_ = result_error.code();
    return *this;
  }

  const std::string str() const {
    std::string str = ss_.str();
    if (append_errno_) {
      if (str.empty()) {
        return strerror(errno_);
      }
      return std::move(str) + ": " + strerror(errno_);
    }
    return str;
  }

  Error(const Error&) = delete;
  Error(Error&&) = delete;
  Error& operator=(const Error&) = delete;
  Error& operator=(Error&&) = delete;

 private:
  std::stringstream ss_;
  int errno_;
  bool append_errno_;
};

inline Error ErrnoError() {
  return Error(errno);
}

template <typename T>
using Result = android::base::expected<T, ResultError>;

// Usage: `Result<Success>` as a result type that doesn't contain a value.
// Use `return {}` or `return Success()` to return with success.
using Success = std::monostate;

}  // namespace base
}  // namespace android

base/result_test.cpp

0 → 100644
+357 −0
Original line number Original line Diff line number Diff line
/*
 * 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 "android-base/result.h"

#include "errno.h"

#include <istream>
#include <string>

#include <gtest/gtest.h>

using namespace std::string_literals;

namespace android {
namespace base {

TEST(result, result_accessors) {
  Result<std::string> result = "success";
  ASSERT_TRUE(result);
  ASSERT_TRUE(result.has_value());

  EXPECT_EQ("success", *result);
  EXPECT_EQ("success", result.value());

  EXPECT_EQ('s', result->data()[0]);
}

TEST(result, result_accessors_rvalue) {
  ASSERT_TRUE(Result<std::string>("success"));
  ASSERT_TRUE(Result<std::string>("success").has_value());

  EXPECT_EQ("success", *Result<std::string>("success"));
  EXPECT_EQ("success", Result<std::string>("success").value());

  EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
}

TEST(result, result_success) {
  Result<Success> result = Success();
  ASSERT_TRUE(result);
  ASSERT_TRUE(result.has_value());

  EXPECT_EQ(Success(), *result);
  EXPECT_EQ(Success(), result.value());
}

TEST(result, result_success_rvalue) {
  // Success() doesn't actually create a Result<Success> object, but rather an object that can be
  // implicitly constructed into a Result<Success> object.

  auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
  ASSERT_TRUE(MakeRvalueSuccessResult());
  ASSERT_TRUE(MakeRvalueSuccessResult().has_value());

  EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
  EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
}

TEST(result, result_error) {
  Result<Success> result = Error() << "failure" << 1;
  ASSERT_FALSE(result);
  ASSERT_FALSE(result.has_value());

  EXPECT_EQ(0, result.error().code());
  EXPECT_EQ("failure1", result.error().message());
}

TEST(result, result_error_empty) {
  Result<Success> result = Error();
  ASSERT_FALSE(result);
  ASSERT_FALSE(result.has_value());

  EXPECT_EQ(0, result.error().code());
  EXPECT_EQ("", result.error().message());
}

TEST(result, result_error_rvalue) {
  // Error() and ErrnoError() aren't actually used to create a Result<T> object.
  // Under the hood, they are an intermediate class that can be implicitly constructed into a
  // Result<T>.  This is needed both to create the ostream and because Error() itself, by
  // definition will not know what the type, T, of the underlying Result<T> object that it would
  // create is.

  auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
  ASSERT_FALSE(MakeRvalueErrorResult());
  ASSERT_FALSE(MakeRvalueErrorResult().has_value());

  EXPECT_EQ(0, MakeRvalueErrorResult().error().code());
  EXPECT_EQ("failure1", MakeRvalueErrorResult().error().message());
}

TEST(result, result_errno_error) {
  constexpr int test_errno = 6;
  errno = test_errno;
  Result<Success> result = ErrnoError() << "failure" << 1;

  ASSERT_FALSE(result);
  ASSERT_FALSE(result.has_value());

  EXPECT_EQ(test_errno, result.error().code());
  EXPECT_EQ("failure1: "s + strerror(test_errno), result.error().message());
}

TEST(result, result_errno_error_no_text) {
  constexpr int test_errno = 6;
  errno = test_errno;
  Result<Success> result = ErrnoError();

  ASSERT_FALSE(result);
  ASSERT_FALSE(result.has_value());

  EXPECT_EQ(test_errno, result.error().code());
  EXPECT_EQ(strerror(test_errno), result.error().message());
}

TEST(result, result_error_from_other_result) {
  auto error_text = "test error"s;
  Result<Success> result = Error() << error_text;

  ASSERT_FALSE(result);
  ASSERT_FALSE(result.has_value());

  Result<std::string> result2 = result.error();

  ASSERT_FALSE(result2);
  ASSERT_FALSE(result2.has_value());

  EXPECT_EQ(0, result.error().code());
  EXPECT_EQ(error_text, result.error().message());
}

TEST(result, result_error_through_ostream) {
  auto error_text = "test error"s;
  Result<Success> result = Error() << error_text;

  ASSERT_FALSE(result);
  ASSERT_FALSE(result.has_value());

  Result<std::string> result2 = Error() << result.error();

  ASSERT_FALSE(result2);
  ASSERT_FALSE(result2.has_value());

  EXPECT_EQ(0, result.error().code());
  EXPECT_EQ(error_text, result.error().message());
}

TEST(result, result_errno_error_through_ostream) {
  auto error_text = "test error"s;
  constexpr int test_errno = 6;
  errno = 6;
  Result<Success> result = ErrnoError() << error_text;

  errno = 0;

  ASSERT_FALSE(result);
  ASSERT_FALSE(result.has_value());

  Result<std::string> result2 = Error() << result.error();

  ASSERT_FALSE(result2);
  ASSERT_FALSE(result2.has_value());

  EXPECT_EQ(test_errno, result.error().code());
  EXPECT_EQ(error_text + ": " + strerror(test_errno), result.error().message());
}

TEST(result, constructor_forwarding) {
  auto result = Result<std::string>(std::in_place, 5, 'a');

  ASSERT_TRUE(result);
  ASSERT_TRUE(result.has_value());

  EXPECT_EQ("aaaaa", *result);
}

struct ConstructorTracker {
  static size_t constructor_called;
  static size_t copy_constructor_called;
  static size_t move_constructor_called;
  static size_t copy_assignment_called;
  static size_t move_assignment_called;

  template <typename T>
  ConstructorTracker(T&& string) : string(string) {
    ++constructor_called;
  }

  ConstructorTracker(const ConstructorTracker& ct) {
    ++copy_constructor_called;
    string = ct.string;
  }
  ConstructorTracker(ConstructorTracker&& ct) noexcept {
    ++move_constructor_called;
    string = std::move(ct.string);
  }
  ConstructorTracker& operator=(const ConstructorTracker& ct) {
    ++copy_assignment_called;
    string = ct.string;
    return *this;
  }
  ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
    ++move_assignment_called;
    string = std::move(ct.string);
    return *this;
  }

  std::string string;
};

size_t ConstructorTracker::constructor_called = 0;
size_t ConstructorTracker::copy_constructor_called = 0;
size_t ConstructorTracker::move_constructor_called = 0;
size_t ConstructorTracker::copy_assignment_called = 0;
size_t ConstructorTracker::move_assignment_called = 0;

Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
  if (in.empty()) {
    return "literal string";
  }
  if (in == "test2") {
    return ConstructorTracker(in + in + "2");
  }
  ConstructorTracker result(in + " " + in);
  return result;
};

TEST(result, no_copy_on_return) {
  // If returning parameters that may be used to implicitly construct the type T of Result<T>,
  // then those parameters are forwarded to the construction of Result<T>.

  // If returning an prvalue or xvalue, it will be move constructed during the construction of
  // Result<T>.

  // This check ensures that that is the case, and particularly that no copy constructors
  // are called.

  auto result1 = ReturnConstructorTracker("");
  ASSERT_TRUE(result1);
  EXPECT_EQ("literal string", result1->string);
  EXPECT_EQ(1U, ConstructorTracker::constructor_called);
  EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
  EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
  EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
  EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);

  auto result2 = ReturnConstructorTracker("test2");
  ASSERT_TRUE(result2);
  EXPECT_EQ("test2test22", result2->string);
  EXPECT_EQ(2U, ConstructorTracker::constructor_called);
  EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
  EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
  EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
  EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);

  auto result3 = ReturnConstructorTracker("test3");
  ASSERT_TRUE(result3);
  EXPECT_EQ("test3 test3", result3->string);
  EXPECT_EQ(3U, ConstructorTracker::constructor_called);
  EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
  EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
  EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
  EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
}

// Below two tests require that we do not hide the move constructor with our forwarding reference
// constructor.  This is done with by disabling the forwarding reference constructor if its first
// and only type is Result<T>.
TEST(result, result_result_with_success) {
  auto return_result_result_with_success = []() -> Result<Result<Success>> {
    return Result<Success>();
  };
  auto result = return_result_result_with_success();
  ASSERT_TRUE(result);
  ASSERT_TRUE(*result);

  auto inner_result = result.value();
  ASSERT_TRUE(inner_result);
}

TEST(result, result_result_with_failure) {
  auto return_result_result_with_error = []() -> Result<Result<Success>> {
    return Result<Success>(ResultError("failure string", 6));
  };
  auto result = return_result_result_with_error();
  ASSERT_TRUE(result);
  ASSERT_FALSE(*result);
  EXPECT_EQ("failure string", (*result).error().message());
  EXPECT_EQ(6, (*result).error().code());
}

// This test requires that we disable the forwarding reference constructor if Result<T> is the
// *only* type that we are forwarding.  In otherwords, if we are forwarding Result<T>, int to
// construct a Result<T>, then we still need the constructor.
TEST(result, result_two_parameter_constructor_same_type) {
  struct TestStruct {
    TestStruct(int value) : value_(value) {}
    TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
    int value_;
  };

  auto return_test_struct = []() -> Result<TestStruct> {
    return Result<TestStruct>(std::in_place, Result<TestStruct>(std::in_place, 6), 6);
  };

  auto result = return_test_struct();
  ASSERT_TRUE(result);
  EXPECT_EQ(36, result->value_);
}

TEST(result, die_on_access_failed_result) {
  Result<std::string> result = Error();
  ASSERT_DEATH(*result, "");
}

TEST(result, die_on_get_error_succesful_result) {
  Result<std::string> result = "success";
  ASSERT_DEATH(result.error(), "");
}

template <class CharT>
std::basic_ostream<CharT>& SetErrnoToTwo(std::basic_ostream<CharT>& ss) {
  errno = 2;
  return ss;
}

TEST(result, preserve_errno) {
  errno = 1;
  int old_errno = errno;
  Result<int> result = Error() << "Failed" << SetErrnoToTwo<char>;
  ASSERT_FALSE(result);
  EXPECT_EQ(old_errno, errno);

  errno = 1;
  old_errno = errno;
  Result<int> result2 = ErrnoError() << "Failed" << SetErrnoToTwo<char>;
  ASSERT_FALSE(result2);
  EXPECT_EQ(old_errno, errno);
  EXPECT_EQ(old_errno, result2.error().code());
}

}  // namespace base
}  // namespace android
+0 −1
Original line number Original line Diff line number Diff line
@@ -190,7 +190,6 @@ cc_test {
        "persistent_properties_test.cpp",
        "persistent_properties_test.cpp",
        "property_service_test.cpp",
        "property_service_test.cpp",
        "property_type_test.cpp",
        "property_type_test.cpp",
        "result_test.cpp",
        "rlimit_parser_test.cpp",
        "rlimit_parser_test.cpp",
        "service_test.cpp",
        "service_test.cpp",
        "subcontext_test.cpp",
        "subcontext_test.cpp",
+2 −2
Original line number Original line Diff line number Diff line
@@ -127,7 +127,7 @@ void Action::ExecuteCommand(const Command& command) const {
    // report such failures unless we're running at the DEBUG log level.
    // report such failures unless we're running at the DEBUG log level.
    bool report_failure = !result.has_value();
    bool report_failure = !result.has_value();
    if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
    if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
        result.error().as_errno == ENOENT) {
        result.error().code() == ENOENT) {
        report_failure = false;
        report_failure = false;
    }
    }


@@ -139,7 +139,7 @@ void Action::ExecuteCommand(const Command& command) const {


        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
                  << ":" << command.line() << ") took " << duration.count() << "ms and "
                  << ":" << command.line() << ") took " << duration.count() << "ms and "
                  << (result ? "succeeded" : "failed: " + result.error().as_string);
                  << (result ? "succeeded" : "failed: " + result.error().message());
    }
    }
}
}


Loading