about summary refs log blame commit diff
path: root/absl/types/any_exception_safety_test.cc
blob: f4ca52eba3b072c4cab97d39a0b37a07937afb21 (plain) (tree)












































































                                                                               
                                      


























































































































                                                                                
// Copyright 2017 The Abseil Authors.
//
// 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 "absl/types/any.h"

#include <typeinfo>
#include <vector>

#include "gtest/gtest.h"
#include "absl/base/internal/exception_safety_testing.h"

using Thrower = absl::ThrowingValue<>;
using ThrowerList = std::initializer_list<Thrower>;
using ThrowerVec = std::vector<Thrower>;
using ThrowingAlloc = absl::ThrowingAllocator<Thrower>;
using ThrowingThrowerVec = std::vector<Thrower, ThrowingAlloc>;

namespace absl {

testing::AssertionResult AbslCheckInvariants(absl::any* a,
                                             InternalAbslNamespaceFinder) {
  using testing::AssertionFailure;
  using testing::AssertionSuccess;

  if (a->has_value()) {
    if (a->type() == typeid(void)) {
      return AssertionFailure()
             << "A non-empty any should not have type `void`";
    }
  } else {
    if (a->type() != typeid(void)) {
      return AssertionFailure()
             << "An empty any should have type void, but has type "
             << a->type().name();
    }
  }

  //  Make sure that reset() changes any to a valid state.
  a->reset();
  if (a->has_value()) {
    return AssertionFailure() << "A reset `any` should be valueless";
  }
  if (a->type() != typeid(void)) {
    return AssertionFailure() << "A reset `any` should have type() of `void`, "
                                 "but instead has type "
                              << a->type().name();
  }
  try {
    auto unused = absl::any_cast<Thrower>(*a);
    static_cast<void>(unused);
    return AssertionFailure()
           << "A reset `any` should not be able to be any_cast";
  } catch (absl::bad_any_cast) {
  } catch (...) {
    return AssertionFailure()
           << "Unexpected exception thrown from absl::any_cast";
  }
  return AssertionSuccess();
}

}  // namespace absl

namespace {

class AnyExceptionSafety : public ::testing::Test {
 private:
  absl::ConstructorTracker inspector_;
};

testing::AssertionResult AnyIsEmpty(absl::any* a) {
  if (!a->has_value()) return testing::AssertionSuccess();
  return testing::AssertionFailure()
         << "a should be empty, but instead has value "
         << absl::any_cast<Thrower>(*a).Get();
}

TEST_F(AnyExceptionSafety, Ctors) {
  Thrower val(1);
  auto with_val = absl::TestThrowingCtor<absl::any>(val);
  auto copy = absl::TestThrowingCtor<absl::any>(with_val);
  auto in_place =
      absl::TestThrowingCtor<absl::any>(absl::in_place_type_t<Thrower>(), 1);
  auto in_place_list = absl::TestThrowingCtor<absl::any>(
      absl::in_place_type_t<ThrowerVec>(), ThrowerList{val});
  auto in_place_list_again =
      absl::TestThrowingCtor<absl::any,
                             absl::in_place_type_t<ThrowingThrowerVec>,
                             ThrowerList, ThrowingAlloc>(
          absl::in_place_type_t<ThrowingThrowerVec>(), {val}, ThrowingAlloc());
}

struct OneFactory {
  std::unique_ptr<absl::any> operator()() const {
    return absl::make_unique<absl::any>(absl::in_place_type_t<Thrower>(), 1,
                                        absl::no_throw_ctor);
  }
};

struct EmptyFactory {
  std::unique_ptr<absl::any> operator()() const {
    return absl::make_unique<absl::any>();
  }
};

TEST_F(AnyExceptionSafety, Assignment) {
  auto thrower_comp = [](const absl::any& l, const absl::any& r) {
    return absl::any_cast<Thrower>(l) == absl::any_cast<Thrower>(r);
  };

  OneFactory one_factory;

  absl::ThrowingValue<absl::NoThrow::kMoveCtor | absl::NoThrow::kMoveAssign>
      moveable_val(2);
  Thrower val(2);
  absl::any any_val(val);

  EXPECT_TRUE(absl::TestExceptionSafety(
      one_factory, [&any_val](absl::any* ap) { *ap = any_val; },
      absl::StrongGuarantee(one_factory, thrower_comp)));

  EXPECT_TRUE(absl::TestExceptionSafety(
      one_factory, [&val](absl::any* ap) { *ap = val; },
      absl::StrongGuarantee(one_factory, thrower_comp)));

  EXPECT_TRUE(absl::TestExceptionSafety(
      one_factory, [&val](absl::any* ap) { *ap = std::move(val); },
      absl::StrongGuarantee(one_factory, thrower_comp)));

  EXPECT_TRUE(absl::TestExceptionSafety(
      one_factory,
      [&moveable_val](absl::any* ap) { *ap = std::move(moveable_val); },
      absl::StrongGuarantee(one_factory, thrower_comp)));

  EmptyFactory empty_factory;
  auto empty_comp = [](const absl::any& l, const absl::any& r) {
    return !(l.has_value() || r.has_value());
  };

  EXPECT_TRUE(absl::TestExceptionSafety(
      empty_factory, [&any_val](absl::any* ap) { *ap = any_val; },
      absl::StrongGuarantee(empty_factory, empty_comp)));

  EXPECT_TRUE(absl::TestExceptionSafety(
      empty_factory, [&val](absl::any* ap) { *ap = val; },
      absl::StrongGuarantee(empty_factory, empty_comp)));

  EXPECT_TRUE(absl::TestExceptionSafety(
      empty_factory, [&val](absl::any* ap) { *ap = std::move(val); },
      absl::StrongGuarantee(empty_factory, empty_comp)));
}
// libstdc++ std::any fails this test
#if !defined(ABSL_HAVE_STD_ANY)
TEST_F(AnyExceptionSafety, Emplace) {
  OneFactory one_factory;

  EXPECT_TRUE(absl::TestExceptionSafety(
      one_factory, [](absl::any* ap) { ap->emplace<Thrower>(2); }, AnyIsEmpty));

  EXPECT_TRUE(absl::TestExceptionSafety(
      one_factory,
      [](absl::any* ap) {
        ap->emplace<absl::ThrowingValue<absl::NoThrow::kMoveCtor |
                                        absl::NoThrow::kMoveAssign>>(2);
      },
      AnyIsEmpty));

  EXPECT_TRUE(absl::TestExceptionSafety(one_factory,
                                        [](absl::any* ap) {
                                          std::initializer_list<Thrower> il{
                                              Thrower(2, absl::no_throw_ctor)};
                                          ap->emplace<ThrowerVec>(il);
                                        },
                                        AnyIsEmpty));

  EmptyFactory empty_factory;
  EXPECT_TRUE(absl::TestExceptionSafety(
      empty_factory, [](absl::any* ap) { ap->emplace<Thrower>(2); },
      AnyIsEmpty));

  EXPECT_TRUE(absl::TestExceptionSafety(empty_factory,
                                        [](absl::any* ap) {
                                          std::initializer_list<Thrower> il{
                                              Thrower(2, absl::no_throw_ctor)};
                                          ap->emplace<ThrowerVec>(il);
                                        },
                                        AnyIsEmpty));
}
#endif  // ABSL_HAVE_STD_ANY

}  // namespace