/*
 * Copyright (C) 2016 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.
 */

#if defined(HAS_GTEST)
#include <gtest/gtest.h>
#else
#include "gtest_stubs.h"
#endif

#include <string.h>

#include <nvram/core/nvram_manager.h>
#include <nvram/core/persistence.h>

#include "fake_storage.h"

namespace nvram {
namespace {

class NvramManagerTest : public testing::Test {
 protected:
  NvramManagerTest() {
    storage::Clear();
  }

  static void SetupHeader(uint32_t header_version, uint32_t index) {
    NvramHeader header;
    header.version = header_version;
    ASSERT_TRUE(header.allocated_indices.Resize(1));
    header.allocated_indices[0] = index;
    ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header));
  }

  static void ReadAndCompareSpaceData(NvramManager* nvram,
                                      uint32_t index,
                                      const void* expected_contents,
                                      size_t expected_size) {
    ReadSpaceRequest read_space_request;
    read_space_request.index = index;
    ReadSpaceResponse read_space_response;
    EXPECT_EQ(NV_RESULT_SUCCESS,
              nvram->ReadSpace(read_space_request, &read_space_response));
    ASSERT_EQ(expected_size, read_space_response.buffer.size());
    EXPECT_EQ(0, memcmp(read_space_response.buffer.data(), expected_contents,
                        expected_size));
  }

  static uint32_t GetControlsMask(const Vector<nvram_control_t>& controls) {
    uint32_t mask = 0;
    for (nvram_control_t control : controls) {
      mask |= (1 << control);
    }
    return mask;
  }
};

TEST_F(NvramManagerTest, Init_FromScratch) {
  NvramManager nvram;

  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 1;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(
      NV_RESULT_SPACE_DOES_NOT_EXIST,
      nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response));
}

TEST_F(NvramManagerTest, Init_TrailingStorageBytes) {
  // Set up a pre-existing space and add some trailing bytes.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space));
  Blob space_blob;
  ASSERT_EQ(storage::Status::kSuccess, storage::LoadSpace(1, &space_blob));
  ASSERT_TRUE(space_blob.Resize(space_blob.size() + 10));
  ASSERT_EQ(storage::Status::kSuccess, storage::StoreSpace(1, space_blob));

  // Produce a matching header and append some trailing bytes.
  NvramHeader header;
  header.version = NvramHeader::kVersion;
  ASSERT_TRUE(header.allocated_indices.Resize(1));
  header.allocated_indices[0] = 1;
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header));
  Blob header_blob;
  ASSERT_EQ(storage::Status::kSuccess, storage::LoadHeader(&header_blob));
  ASSERT_TRUE(header_blob.Resize(header_blob.size() + 10));
  ASSERT_EQ(storage::Status::kSuccess, storage::StoreHeader(header_blob));

  // Initialize the |NvramManager| and check that the header and space blobs get
  // loaded successfully.
  NvramManager nvram;

  GetInfoRequest get_info_request;
  GetInfoResponse get_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.GetInfo(get_info_request, &get_info_response));
  ASSERT_EQ(1U, get_info_response.space_list.size());
  EXPECT_EQ(1U, get_info_response.space_list[0]);

  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 1;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response));
  EXPECT_EQ(10U, get_space_info_response.size);
}

TEST_F(NvramManagerTest, Init_SpacesPresent) {
  // Set up two pre-existing spaces.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space));
  ASSERT_TRUE(space.contents.Resize(20));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(2, space));

  // Indicate 3 present spaces in the header, including one that doesn't have
  // space data in storage.
  NvramHeader header;
  header.version = NvramHeader::kVersion;
  ASSERT_TRUE(header.allocated_indices.Resize(3));
  header.allocated_indices[0] = 1;
  header.allocated_indices[1] = 2;
  header.allocated_indices[2] = 3;
  header.provisional_index.Activate() = 4;
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header));

  NvramManager nvram;

  // Check that the spaces are correctly recovered.
  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 1;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response));
  EXPECT_EQ(10u, get_space_info_response.size);

  get_space_info_request.index = 2;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response));
  EXPECT_EQ(20u, get_space_info_response.size);

  get_space_info_request.index = 3;
  EXPECT_EQ(
      NV_RESULT_INTERNAL_ERROR,
      nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response));

  get_space_info_request.index = 4;
  EXPECT_EQ(
      NV_RESULT_SPACE_DOES_NOT_EXIST,
      nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response));
}

TEST_F(NvramManagerTest, Init_BadSpacePresent) {
  // Set up a good and a bad NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space));
  const uint8_t kBadSpaceData[] = {0xba, 0xad};
  Blob bad_space_blob;
  ASSERT_TRUE(bad_space_blob.Assign(kBadSpaceData, sizeof(kBadSpaceData)));
  ASSERT_EQ(storage::Status::kSuccess,
            storage::StoreSpace(2, bad_space_blob));

  NvramHeader header;
  header.version = NvramHeader::kVersion;
  ASSERT_TRUE(header.allocated_indices.Resize(2));
  header.allocated_indices[0] = 1;
  header.allocated_indices[1] = 2;
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header));

  NvramManager nvram;

  // The bad index will fail requests.
  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 2;
  GetSpaceInfoResponse get_space_info_response;
  nvram_result_t result =
      nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response);
  EXPECT_NE(NV_RESULT_SUCCESS, result);
  EXPECT_NE(NV_RESULT_SPACE_DOES_NOT_EXIST, result);

  // A request to get info for the good index should succeed.
  get_space_info_request.index = 1;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response));
  EXPECT_EQ(10u, get_space_info_response.size);
}

TEST_F(NvramManagerTest, Init_NewerStorageVersion) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space));

  SetupHeader(NvramHeader::kVersion + 1, 1);

  NvramManager nvram;

  // Requests should fail due to version mismatch.
  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 1;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(
      NV_RESULT_INTERNAL_ERROR,
      nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response));
}

TEST_F(NvramManagerTest, Init_StorageObjectTypeMismatch) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space));

  // Copy the space blob to the header storage.
  Blob space_blob;
  ASSERT_EQ(storage::Status::kSuccess, storage::LoadSpace(1, &space_blob));
  ASSERT_EQ(storage::Status::kSuccess, storage::StoreHeader(space_blob));

  NvramManager nvram;

  // Initialization should detect that the header storage object doesn't look
  // like a header, so initialization should fail.
  GetInfoRequest get_info_request;
  GetInfoResponse get_info_response;
  EXPECT_EQ(NV_RESULT_INTERNAL_ERROR,
            nvram.GetInfo(get_info_request, &get_info_response));
}

TEST_F(NvramManagerTest, CreateSpace_Success) {
  NvramManager nvram;

  // Make a call to CreateSpace, which should succeed.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 32;
  ASSERT_TRUE(create_space_request.controls.Resize(5));
  create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK;
  create_space_request.controls[1] = NV_CONTROL_BOOT_READ_LOCK;
  create_space_request.controls[2] = NV_CONTROL_WRITE_AUTHORIZATION;
  create_space_request.controls[3] = NV_CONTROL_READ_AUTHORIZATION;
  create_space_request.controls[4] = NV_CONTROL_WRITE_EXTEND;

  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.CreateSpace(create_space_request, &create_space_response));

  // GetSpaceInfo should reflect the space parameters set during creation.
  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 1;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response));

  EXPECT_EQ(32u, get_space_info_response.size);
  EXPECT_EQ(GetControlsMask(create_space_request.controls),
            GetControlsMask(get_space_info_response.controls));
  EXPECT_EQ(false, get_space_info_response.read_locked);
  EXPECT_EQ(false, get_space_info_response.write_locked);
}

TEST_F(NvramManagerTest, CreateSpace_Existing) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space));

  SetupHeader(NvramHeader::kVersion, 1);

  NvramManager nvram;

  // A request to create another space with the same index should fail.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 16;

  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_SPACE_ALREADY_EXISTS,
            nvram.CreateSpace(create_space_request, &create_space_response));
}

TEST_F(NvramManagerTest, CreateSpace_TooLarge) {
  NvramManager nvram;

  // A request to create a space with a too large content size should fail.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 16384;

  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_INVALID_PARAMETER,
            nvram.CreateSpace(create_space_request, &create_space_response));
}

TEST_F(NvramManagerTest, CreateSpace_AuthTooLarge) {
  NvramManager nvram;

  // A request to create a space with a too large authorization value size
  // should fail.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  ASSERT_TRUE(create_space_request.authorization_value.Resize(256));

  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_INVALID_PARAMETER,
            nvram.CreateSpace(create_space_request, &create_space_response));
}

TEST_F(NvramManagerTest, CreateSpace_BadControl) {
  NvramManager nvram;

  // A request to create a space with an unknown control value should fail.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 16;
  ASSERT_TRUE(create_space_request.controls.Resize(2));
  create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK;
  create_space_request.controls[1] = 17;

  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_INVALID_PARAMETER,
            nvram.CreateSpace(create_space_request, &create_space_response));
}

TEST_F(NvramManagerTest, CreateSpace_ControlWriteLockExclusive) {
  NvramManager nvram;

  // Spaces may not be created with conflicting write lock modes.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 16;
  ASSERT_TRUE(create_space_request.controls.Resize(2));
  create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK;
  create_space_request.controls[1] = NV_CONTROL_PERSISTENT_WRITE_LOCK;

  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_INVALID_PARAMETER,
            nvram.CreateSpace(create_space_request, &create_space_response));
}

TEST_F(NvramManagerTest, CreateSpace_WriteExtendSpaceSize) {
  NvramManager nvram;

  // Write-extend spaces must match SHA256 hash size, i.e. 32 bytes.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 16;
  ASSERT_TRUE(create_space_request.controls.Resize(1));
  create_space_request.controls[0] = NV_CONTROL_WRITE_EXTEND;

  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_INVALID_PARAMETER,
            nvram.CreateSpace(create_space_request, &create_space_response));
}

TEST_F(NvramManagerTest, CreateSpace_HeaderWriteError) {
  // If the header fails to get written to storage, the creation request should
  // fail.
  storage::SetHeaderWriteError(true);

  NvramManager nvram;

  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 16;

  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_INTERNAL_ERROR,
            nvram.CreateSpace(create_space_request, &create_space_response));

  // The space shouldn't be present.
  GetInfoRequest get_info_request;
  GetInfoResponse get_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.GetInfo(get_info_request, &get_info_response));
  EXPECT_EQ(0U, get_info_response.space_list.size());

  // Creation of the space after clearing the error should work.
  storage::SetHeaderWriteError(false);
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.CreateSpace(create_space_request, &create_space_response));

  // The space should be reported as allocated now.
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.GetInfo(get_info_request, &get_info_response));
  ASSERT_EQ(1U, get_info_response.space_list.size());
  EXPECT_EQ(1U, get_info_response.space_list[0]);
}

TEST_F(NvramManagerTest, CreateSpace_SpaceWriteError) {
  storage::SetSpaceWriteError(1, true);
  NvramManager nvram;

  // A request to create another space with the same index should fail.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 16;

  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_INTERNAL_ERROR,
            nvram.CreateSpace(create_space_request, &create_space_response));

  // Reloading the state after a crash should not show any traces of the space.
  storage::SetSpaceWriteError(1, false);
  NvramManager nvram2;

  // The space shouldn't exist in the space list.
  GetInfoRequest get_info_request;
  GetInfoResponse get_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram2.GetInfo(get_info_request, &get_info_response));

  EXPECT_EQ(0U, get_info_response.space_list.size());

  // The space info request should indicate the space doesn't exist.
  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 1;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(
      NV_RESULT_SPACE_DOES_NOT_EXIST,
      nvram2.GetSpaceInfo(get_space_info_request, &get_space_info_response));
}

TEST_F(NvramManagerTest, DeleteSpace_SpaceAbsent) {
  NvramManager nvram;

  // Attempt to delete a non-existing space.
  DeleteSpaceRequest delete_space_request;
  delete_space_request.index = 42;
  DeleteSpaceResponse delete_space_response;
  EXPECT_EQ(NV_RESULT_SPACE_DOES_NOT_EXIST,
            nvram.DeleteSpace(delete_space_request, &delete_space_response));
}

TEST_F(NvramManagerTest, DeleteSpace_Success) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space));
  SetupHeader(NvramHeader::kVersion, 42);

  NvramManager nvram;

  // Successful deletion.
  DeleteSpaceRequest delete_space_request;
  delete_space_request.index = 42;
  DeleteSpaceResponse delete_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.DeleteSpace(delete_space_request, &delete_space_response));
}

TEST_F(NvramManagerTest, DeleteSpace_AuthorizationFailure) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_WRITE_AUTHORIZATION);
  const char kAuthorizationValue[] = "secret";
  ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue,
                                               sizeof(kAuthorizationValue)));
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space));
  SetupHeader(NvramHeader::kVersion, 42);

  NvramManager nvram;

  // Deletion should fail if correct secret is not provided.
  DeleteSpaceRequest delete_space_request;
  delete_space_request.index = 42;
  DeleteSpaceResponse delete_space_response;
  EXPECT_EQ(NV_RESULT_ACCESS_DENIED,
            nvram.DeleteSpace(delete_space_request, &delete_space_response));
}

TEST_F(NvramManagerTest, DeleteSpace_HalfDeleted) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space));
  SetupHeader(NvramHeader::kVersion, 42);

  // Hold on to the space data.
  Blob space_data;
  ASSERT_EQ(storage::Status::kSuccess, storage::LoadSpace(42, &space_data));

  NvramManager nvram;

  // Delete the space.
  DeleteSpaceRequest delete_space_request;
  delete_space_request.index = 42;
  DeleteSpaceResponse delete_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.DeleteSpace(delete_space_request, &delete_space_response));

  // Put the space data back into place to simulate a half-completed deletion.
  ASSERT_EQ(storage::Status::kSuccess, storage::StoreSpace(42, space_data));

  // The space should remain deleted after re-initialization.
  NvramManager nvram2;

  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 42;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(
      NV_RESULT_SPACE_DOES_NOT_EXIST,
      nvram2.GetSpaceInfo(get_space_info_request, &get_space_info_response));

  // Re-creation of a space with the same index should work.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 42;
  create_space_request.size = 32;
  ASSERT_TRUE(create_space_request.controls.Resize(1));
  create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK;
  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram2.CreateSpace(create_space_request, &create_space_response));
}

TEST_F(NvramManagerTest, DeleteSpace_SpaceDeleteError) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space));
  SetupHeader(NvramHeader::kVersion, 42);

  // Make space deletion fail.
  storage::SetSpaceWriteError(42, true);

  NvramManager nvram;

  // Attempt to delete the space.
  DeleteSpaceRequest delete_space_request;
  delete_space_request.index = 42;
  DeleteSpaceResponse delete_space_response;
  EXPECT_EQ(NV_RESULT_INTERNAL_ERROR,
            nvram.DeleteSpace(delete_space_request, &delete_space_response));

  // The space should remain present.
  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 42;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response));
  EXPECT_EQ(10U, get_space_info_response.size);

  // Starting up from scratch shouldn't destroy the space either.
  storage::SetSpaceWriteError(42, false);

  NvramManager nvram2;

  GetSpaceInfoResponse get_space_info_response_2;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response_2));
  EXPECT_EQ(10U, get_space_info_response_2.size);
}

TEST_F(NvramManagerTest, DeleteSpace_HeaderWriteError) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(42, space));
  SetupHeader(NvramHeader::kVersion, 42);

  // Header write on deletion will fail.
  storage::SetHeaderWriteError(true);

  NvramManager nvram;

  // Attempt to delete the space.
  DeleteSpaceRequest delete_space_request;
  delete_space_request.index = 42;
  DeleteSpaceResponse delete_space_response;
  EXPECT_EQ(NV_RESULT_INTERNAL_ERROR,
            nvram.DeleteSpace(delete_space_request, &delete_space_response));

  // The space should remain present.
  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 42;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response));
  EXPECT_EQ(10U, get_space_info_response.size);

  // Starting up from scratch shouldn't destroy the space either.
  storage::SetSpaceWriteError(42, false);

  NvramManager nvram2;

  GetSpaceInfoResponse get_space_info_response_2;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response_2));
  EXPECT_EQ(10U, get_space_info_response_2.size);
}

TEST_F(NvramManagerTest, DisableCreate_Success) {
  NvramManager nvram;

  // Issue a successful disable create request.
  DisableCreateRequest disable_create_request;
  DisableCreateResponse disable_create_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.DisableCreate(disable_create_request,
                                                   &disable_create_response));

  // Make sure space creation request fail afterwards.
  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 32;
  ASSERT_TRUE(create_space_request.controls.Resize(1));
  create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK;
  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_OPERATION_DISABLED,
            nvram.CreateSpace(create_space_request, &create_space_response));

  // Redundant requests to disable creation are OK.
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.DisableCreate(disable_create_request,
                                                   &disable_create_response));

  // Space creation should remain disabled even after a reboot.
  NvramManager nvram2;
  EXPECT_EQ(NV_RESULT_OPERATION_DISABLED,
            nvram2.CreateSpace(create_space_request, &create_space_response));
}

TEST_F(NvramManagerTest, DisableCreate_WriteError) {
  // Make header writes fail.
  storage::SetHeaderWriteError(true);

  NvramManager nvram;

  // The disable request should fail.
  DisableCreateRequest disable_create_request;
  DisableCreateResponse disable_create_response;
  EXPECT_EQ(
      NV_RESULT_INTERNAL_ERROR,
      nvram.DisableCreate(disable_create_request, &disable_create_response));

  // We should still be able to create spaces after clearing the error.
  storage::SetHeaderWriteError(false);

  CreateSpaceRequest create_space_request;
  create_space_request.index = 1;
  create_space_request.size = 32;
  ASSERT_TRUE(create_space_request.controls.Resize(1));
  create_space_request.controls[0] = NV_CONTROL_BOOT_WRITE_LOCK;
  CreateSpaceResponse create_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.CreateSpace(create_space_request, &create_space_response));
}

TEST_F(NvramManagerTest, WriteSpace_SpaceAbsent) {
  NvramManager nvram;

  // Attempt to write a non-existing space.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("0123456789", 10));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_SPACE_DOES_NOT_EXIST,
            nvram.WriteSpace(write_space_request, &write_space_response));
}

TEST_F(NvramManagerTest, WriteSpace_Success) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Write the space.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("0123456789", 10));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.WriteSpace(write_space_request,
                                                &write_space_response));

  // Read back the space and compare contents.
  ReadAndCompareSpaceData(&nvram, 17, "0123456789", 10);

  // The data should persist even after a reboot.
  NvramManager nvram2;

  ReadAndCompareSpaceData(&nvram2, 17, "0123456789", 10);
}

TEST_F(NvramManagerTest, WriteSpace_ExcessData) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Write the space.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("0123456789abcdef", 16));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_INVALID_PARAMETER,
            nvram.WriteSpace(write_space_request, &write_space_response));
}

TEST_F(NvramManagerTest, WriteSpace_ShortData) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  memset(space.contents.data(), 'X', space.contents.size());
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Write the space.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("01234", 5));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.WriteSpace(write_space_request, &write_space_response));

  // Read back the space data and verify that the missing content bytes have
  // been set to 0.
  const uint8_t kExpectedContents[] = {'0', '1', '2', '3', '4', 0, 0, 0, 0, 0};
  ReadAndCompareSpaceData(&nvram, 17, kExpectedContents, 10);
}

TEST_F(NvramManagerTest, WriteSpace_WriteExtend) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_WRITE_EXTEND);
  ASSERT_TRUE(space.contents.Resize(32));
  memset(space.contents.data(), 0, space.contents.size());
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Write the space.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("data", 4));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.WriteSpace(write_space_request, &write_space_response));

  // Read back the space data and verify the hash.
  const uint8_t kExpectedContents[] = {
      0xee, 0x84, 0x52, 0x88, 0xbb, 0x60, 0x7e, 0x02, 0xfd, 0xfb, 0x31,
      0x95, 0x3a, 0x77, 0x23, 0xcf, 0x67, 0xea, 0x6e, 0x2d, 0xd7, 0xdb,
      0x8c, 0xb4, 0xe4, 0xd2, 0xfd, 0xb4, 0x76, 0x7a, 0x67, 0x89,
  };
  ReadAndCompareSpaceData(&nvram, 17, kExpectedContents, 32);
}

TEST_F(NvramManagerTest, WriteSpace_WriteExtendShortSpace) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_WRITE_EXTEND);
  ASSERT_TRUE(space.contents.Resize(16));
  memset(space.contents.data(), 0, space.contents.size());
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Write the space.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("data", 4));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.WriteSpace(write_space_request, &write_space_response));

  // Read back the space data and verify the truncated hash.
  const uint8_t kExpectedContents[] = {
      0x24, 0x2a, 0xbb, 0x36, 0x10, 0x37, 0x92, 0x3f,
      0x7d, 0x7d, 0x92, 0x3a, 0x16, 0x65, 0xd2, 0xa2,
  };
  ReadAndCompareSpaceData(&nvram, 17, kExpectedContents, 16);
}

TEST_F(NvramManagerTest, WriteSpace_WriteExtendLongSpace) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_WRITE_EXTEND);
  ASSERT_TRUE(space.contents.Resize(33));
  memset(space.contents.data(), 'X', space.contents.size());
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Write the space.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("data", 4));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.WriteSpace(write_space_request, &write_space_response));

  // Read back the space data and verify the hash and trailing 0 bytes.
  const uint8_t kExpectedContents[] = {
      0x99, 0xb8, 0x5f, 0xd0, 0xf7, 0x9b, 0x17, 0x2e, 0x0e, 0x58, 0x3d,
      0x3c, 0x9a, 0x29, 0xa3, 0xaf, 0x0a, 0x4c, 0x68, 0x97, 0x72, 0x8c,
      0x0c, 0xa4, 0x37, 0xad, 0x39, 0xf3, 0x8c, 0x6e, 0x64, 0xd7, 0x00,
  };
  ReadAndCompareSpaceData(&nvram, 17, kExpectedContents, 33);
}

TEST_F(NvramManagerTest, WriteSpace_AuthorizationFailure) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_WRITE_AUTHORIZATION);
  ASSERT_TRUE(space.contents.Resize(10));
  const char kAuthorizationValue[] = "secret";
  ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue,
                                               sizeof(kAuthorizationValue)));
  ASSERT_TRUE(space.contents.Assign("0123456789", 10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Attempt a write with the wrong authorization value.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("data", 4));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_ACCESS_DENIED,
            nvram.WriteSpace(write_space_request, &write_space_response));

  // The previous data should remain effective.
  ReadAndCompareSpaceData(&nvram, 17, "0123456789", 10);
}

TEST_F(NvramManagerTest, WriteSpace_WriteError) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Assign("0123456789", 10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  storage::SetSpaceWriteError(17, true);

  // Attempt a write, which should fail.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("data", 4));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_INTERNAL_ERROR,
            nvram.WriteSpace(write_space_request, &write_space_response));

  // The previous data should remain effective.
  ReadAndCompareSpaceData(&nvram, 17, "0123456789", 10);
}

TEST_F(NvramManagerTest, ReadSpace_SpaceAbsent) {
  NvramManager nvram;

  // Attempt a read from a space that doesn't exist.
  ReadSpaceRequest read_space_request;
  read_space_request.index = 17;
  ReadSpaceResponse read_space_response;
  EXPECT_EQ(NV_RESULT_SPACE_DOES_NOT_EXIST,
            nvram.ReadSpace(read_space_request, &read_space_response));
}

TEST_F(NvramManagerTest, ReadSpace_AuthorizationFailure) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_READ_AUTHORIZATION);
  ASSERT_TRUE(space.contents.Resize(10));
  const char kAuthorizationValue[] = "secret";
  ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue,
                                               sizeof(kAuthorizationValue)));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Attempt a read from the space.
  ReadSpaceRequest read_space_request;
  read_space_request.index = 17;
  ReadSpaceResponse read_space_response;
  EXPECT_EQ(NV_RESULT_ACCESS_DENIED,
            nvram.ReadSpace(read_space_request, &read_space_response));
  EXPECT_EQ(0U, read_space_response.buffer.size());
}

TEST_F(NvramManagerTest, LockSpaceWrite_SpaceAbsent) {
  NvramManager nvram;

  // Attempt to lock a space that doesn't exist.
  LockSpaceWriteRequest lock_space_write_request;
  lock_space_write_request.index = 17;
  LockSpaceWriteResponse lock_space_write_response;
  EXPECT_EQ(NV_RESULT_SPACE_DOES_NOT_EXIST,
            nvram.LockSpaceWrite(lock_space_write_request,
                                 &lock_space_write_response));
}

TEST_F(NvramManagerTest, LockSpaceWrite_AuthorizationFailure) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK) |
                   (1 << NV_CONTROL_WRITE_AUTHORIZATION);
  ASSERT_TRUE(space.contents.Resize(10));
  const char kAuthorizationValue[] = "secret";
  ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue,
                                               sizeof(kAuthorizationValue)));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Attempt to lock a space without valid authentication.
  LockSpaceWriteRequest lock_space_write_request;
  lock_space_write_request.index = 17;
  LockSpaceWriteResponse lock_space_write_response;
  EXPECT_EQ(NV_RESULT_ACCESS_DENIED,
            nvram.LockSpaceWrite(lock_space_write_request,
                                 &lock_space_write_response));
}

TEST_F(NvramManagerTest, LockSpaceWrite_SuccessPersistent) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK);
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Lock the space.
  LockSpaceWriteRequest lock_space_write_request;
  lock_space_write_request.index = 17;
  LockSpaceWriteResponse lock_space_write_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.LockSpaceWrite(lock_space_write_request,
                                 &lock_space_write_response));

  // Writing should fail now.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("data", 4));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_OPERATION_DISABLED,
            nvram.WriteSpace(write_space_request, &write_space_response));

  // The lock should be persistent, so writing should fail after reboot.
  NvramManager nvram2;

  EXPECT_EQ(NV_RESULT_OPERATION_DISABLED,
            nvram2.WriteSpace(write_space_request, &write_space_response));
}

TEST_F(NvramManagerTest, LockSpaceWrite_SuccessBoot) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_BOOT_WRITE_LOCK);
  ASSERT_TRUE(space.contents.Assign("01234567890", 10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Lock the space.
  LockSpaceWriteRequest lock_space_write_request;
  lock_space_write_request.index = 17;
  LockSpaceWriteResponse lock_space_write_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.LockSpaceWrite(lock_space_write_request,
                                 &lock_space_write_response));

  // Writing should fail now.
  WriteSpaceRequest write_space_request;
  write_space_request.index = 17;
  ASSERT_TRUE(write_space_request.buffer.Assign("newcontent", 10));
  WriteSpaceResponse write_space_response;
  EXPECT_EQ(NV_RESULT_OPERATION_DISABLED,
            nvram.WriteSpace(write_space_request, &write_space_response));

  // We configured a per-boot lock, so writing should succeed after reboot.
  NvramManager nvram2;

  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram2.WriteSpace(write_space_request, &write_space_response));
  ReadAndCompareSpaceData(&nvram2, 17, "newcontent", 10);
}

TEST_F(NvramManagerTest, LockSpaceWrite_NotLockable) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Attempt to lock a space without valid authentication.
  LockSpaceWriteRequest lock_space_write_request;
  lock_space_write_request.index = 17;
  LockSpaceWriteResponse lock_space_write_response;
  EXPECT_EQ(NV_RESULT_INVALID_PARAMETER,
            nvram.LockSpaceWrite(lock_space_write_request,
                                 &lock_space_write_response));
}

TEST_F(NvramManagerTest, LockSpaceRead_SpaceAbsent) {
  NvramManager nvram;

  // Attempt to lock a non-existing space.
  LockSpaceReadRequest lock_space_read_request;
  lock_space_read_request.index = 17;
  LockSpaceReadResponse lock_space_read_response;
  EXPECT_EQ(
      NV_RESULT_SPACE_DOES_NOT_EXIST,
      nvram.LockSpaceRead(lock_space_read_request, &lock_space_read_response));
}

TEST_F(NvramManagerTest, LockSpaceRead_AuthorizationFailure) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls =
      (1 << NV_CONTROL_BOOT_READ_LOCK) | (1 << NV_CONTROL_READ_AUTHORIZATION);
  ASSERT_TRUE(space.contents.Resize(10));
  const char kAuthorizationValue[] = "secret";
  ASSERT_TRUE(space.authorization_value.Assign(kAuthorizationValue,
                                               sizeof(kAuthorizationValue)));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Attempt to lock a space without valid authorization.
  LockSpaceReadRequest lock_space_read_request;
  lock_space_read_request.index = 17;
  LockSpaceReadResponse lock_space_read_response;
  EXPECT_EQ(
      NV_RESULT_ACCESS_DENIED,
      nvram.LockSpaceRead(lock_space_read_request, &lock_space_read_response));
}

TEST_F(NvramManagerTest, LockSpaceRead_Success) {
  // Set up an NVRAM space.
  NvramSpace space;
  space.controls = (1 << NV_CONTROL_BOOT_READ_LOCK);
  ASSERT_TRUE(space.contents.Assign("0123456789", 10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Lock the space.
  LockSpaceReadRequest lock_space_read_request;
  lock_space_read_request.index = 17;
  LockSpaceReadResponse lock_space_read_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.LockSpaceRead(lock_space_read_request,
                                                   &lock_space_read_response));

  // Read requests should fail now.
  ReadSpaceRequest read_space_request;
  read_space_request.index = 17;
  ReadSpaceResponse read_space_response;
  EXPECT_EQ(NV_RESULT_OPERATION_DISABLED,
            nvram.ReadSpace(read_space_request, &read_space_response));
  EXPECT_EQ(0U, read_space_response.buffer.size());

  // This is a non-persistent lock, so reads should work again after a reboot.
  NvramManager nvram2;

  ReadAndCompareSpaceData(&nvram2, 17, "0123456789", 10);
}

TEST_F(NvramManagerTest, LockSpaceRead_NotLockable) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Attempt to lock a space without valid authorization.
  LockSpaceReadRequest lock_space_read_request;
  lock_space_read_request.index = 17;
  LockSpaceReadResponse lock_space_read_response;
  EXPECT_EQ(
      NV_RESULT_INVALID_PARAMETER,
      nvram.LockSpaceRead(lock_space_read_request, &lock_space_read_response));
}

TEST_F(NvramManagerTest, WipeStorage_Success) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  // Check that the space is visible.
  NvramManager nvram;
  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 17;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response));
  EXPECT_EQ(10U, get_space_info_response.size);

  // Request a wipe.
  WipeStorageRequest wipe_storage_request;
  WipeStorageResponse wipe_storage_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.WipeStorage(wipe_storage_request, &wipe_storage_response));

  // The space should no longer be declared.
  GetInfoRequest get_info_request;
  GetInfoResponse get_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.GetInfo(get_info_request, &get_info_response));
  EXPECT_EQ(0U, get_info_response.space_list.size());

  // Accessing the space should fail.
  EXPECT_EQ(
      NV_RESULT_SPACE_DOES_NOT_EXIST,
      nvram.GetSpaceInfo(get_space_info_request, &get_space_info_response));
}

TEST_F(NvramManagerTest, WipeStorage_Abort) {
  // Set up two pre-existing spaces and a matching header.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(1, space));
  ASSERT_TRUE(space.contents.Resize(20));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(2, space));
  NvramHeader header;
  header.version = NvramHeader::kVersion;
  ASSERT_TRUE(header.allocated_indices.Resize(2));
  header.allocated_indices[0] = 1;
  header.allocated_indices[1] = 2;
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreHeader(header));

  // Check that the spaces are visible.
  NvramManager nvram;
  GetInfoRequest get_info_request;
  GetInfoResponse get_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.GetInfo(get_info_request, &get_info_response));
  EXPECT_EQ(2U, get_info_response.space_list.size());
  int space_mask = 0;
  for (size_t i = 0; i < get_info_response.space_list.size(); ++i) {
    space_mask |= (1 << get_info_response.space_list[i]);
  }
  EXPECT_EQ(0x6, space_mask);

  // Set things up so the deletion request for the second space fails.
  storage::SetSpaceWriteError(2, true);

  // The wipe request should fail now.
  WipeStorageRequest wipe_storage_request;
  WipeStorageResponse wipe_storage_response;
  EXPECT_EQ(NV_RESULT_INTERNAL_ERROR,
            nvram.WipeStorage(wipe_storage_request, &wipe_storage_response));

  // New wipe attempt with a fresh instance after clearing the error.
  storage::SetSpaceWriteError(2, false);
  NvramManager nvram2;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram2.WipeStorage(wipe_storage_request, &wipe_storage_response));

  // No spaces should remain.
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram2.GetInfo(get_info_request, &get_info_response));
  EXPECT_EQ(0U, get_info_response.space_list.size());
}

TEST_F(NvramManagerTest, WipeStorage_Disable) {
  // Set up an NVRAM space.
  NvramSpace space;
  ASSERT_TRUE(space.contents.Resize(10));
  ASSERT_EQ(storage::Status::kSuccess, persistence::StoreSpace(17, space));
  SetupHeader(NvramHeader::kVersion, 17);

  NvramManager nvram;

  // Disable wiping.
  DisableWipeRequest disable_wipe_request;
  DisableWipeResponse disable_wipe_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.DisableWipe(disable_wipe_request, &disable_wipe_response));

  // A wipe request should fail.
  WipeStorageRequest wipe_storage_request;
  WipeStorageResponse wipe_storage_response;
  EXPECT_EQ(NV_RESULT_OPERATION_DISABLED,
            nvram.WipeStorage(wipe_storage_request, &wipe_storage_response));

  // The space should remain declared.
  GetInfoRequest get_info_request;
  GetInfoResponse get_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS,
            nvram.GetInfo(get_info_request, &get_info_response));
  ASSERT_EQ(1U, get_info_response.space_list.size());
  EXPECT_EQ(17U, get_info_response.space_list[0]);

  // The space data should remain present.
  GetSpaceInfoRequest get_space_info_request;
  get_space_info_request.index = 17;
  GetSpaceInfoResponse get_space_info_response;
  EXPECT_EQ(NV_RESULT_SUCCESS, nvram.GetSpaceInfo(get_space_info_request,
                                                  &get_space_info_response));
  EXPECT_EQ(10U, get_space_info_response.size);
}

}  // namespace
}  // namespace nvram