// Copyright 2025 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "iree/io/file_contents.h"

#include "iree/base/api.h"

#if IREE_FILE_IO_ENABLE

#include <cstring>
#include <string>

#include "iree/testing/gtest.h"
#include "iree/testing/status_matchers.h"
#include "iree/testing/temp_file.h"

namespace iree {
namespace io {
namespace {

using ::iree::testing::status::StatusIs;

std::string GetUniqueContents(const char* unique_name,
                              iree_host_size_t padded_size) {
  std::string str = std::string("Test with name ") + unique_name + "\n";
  if (str.size() < padded_size) str.resize(padded_size, 0);
  return str;
}

TEST(FileContents, ReadWriteContentsPreload) {
  constexpr const char* kUniqueName = "ReadWriteContents";
  iree::testing::TempFilePath path("iree_file_contents_test");

  // File must not exist.
  ASSERT_FALSE(path.Exists());

  // Generate file contents.
  auto write_contents = GetUniqueContents(kUniqueName, 32);

  // Write the contents to disk.
  IREE_ASSERT_OK(iree_io_file_contents_write(
      path.path_view(),
      iree_make_const_byte_span(write_contents.data(), write_contents.size()),
      iree_allocator_system()));

  // Read the contents from disk.
  iree_io_file_contents_t* read_contents = NULL;
  IREE_ASSERT_OK(iree_io_file_contents_read(
      path.path_view(), iree_allocator_system(), &read_contents));

  // Expect the contents are equal.
  EXPECT_EQ(write_contents.size(), read_contents->const_buffer.data_length);
  EXPECT_EQ(memcmp(write_contents.data(), read_contents->const_buffer.data,
                   read_contents->const_buffer.data_length),
            0);

  iree_io_file_contents_free(read_contents);
}

TEST(FileContents, ReadWriteContentsMmap) {
  constexpr const char* kUniqueName = "ReadWriteContents";
  iree::testing::TempFilePath path("iree_file_contents_test");

  // File must not exist.
  ASSERT_FALSE(path.Exists());

  // Generate file contents.
  auto write_contents = GetUniqueContents(kUniqueName, 4096);

  // Write the contents to disk.
  IREE_ASSERT_OK(iree_io_file_contents_write(
      path.path_view(),
      iree_make_const_byte_span(write_contents.data(), write_contents.size()),
      iree_allocator_system()));

  // Read the contents from disk.
  iree_io_file_contents_t* read_contents = NULL;
  IREE_ASSERT_OK(
      iree_io_file_contents_map(path.path_view(), IREE_IO_FILE_ACCESS_READ,
                                iree_allocator_system(), &read_contents));

  // Expect the contents are equal.
  EXPECT_EQ(write_contents.size(), read_contents->const_buffer.data_length);
  EXPECT_EQ(memcmp(write_contents.data(), read_contents->const_buffer.data,
                   read_contents->const_buffer.data_length),
            0);

  iree_io_file_contents_free(read_contents);
}

// Tests that a file opened for reading can be opened again concurrently.
// Validates FILE_SHARE_READ behavior on Windows. Without it, the second open
// fails with ERROR_SHARING_VIOLATION.
TEST(FileContents, ConcurrentReadOpens) {
  constexpr const char* kUniqueName = "ConcurrentReadOpens";
  iree::testing::TempFilePath path("iree_file_contents_test");

  // File must not exist.
  ASSERT_FALSE(path.Exists());

  // Write a file to open.
  auto contents = GetUniqueContents(kUniqueName, 4096);
  IREE_ASSERT_OK(iree_io_file_contents_write(
      path.path_view(),
      iree_make_const_byte_span(contents.data(), contents.size()),
      iree_allocator_system()));

  // Open the file twice for reading. Both should succeed.
  iree_io_file_contents_t* read1 = NULL;
  IREE_ASSERT_OK(iree_io_file_contents_map(path.path_view(),
                                           IREE_IO_FILE_ACCESS_READ,
                                           iree_allocator_system(), &read1));

  iree_io_file_contents_t* read2 = NULL;
  IREE_ASSERT_OK(iree_io_file_contents_map(path.path_view(),
                                           IREE_IO_FILE_ACCESS_READ,
                                           iree_allocator_system(), &read2));

  // Both should have the same contents.
  EXPECT_EQ(read1->const_buffer.data_length, read2->const_buffer.data_length);
  EXPECT_EQ(memcmp(read1->const_buffer.data, read2->const_buffer.data,
                   read1->const_buffer.data_length),
            0);

  iree_io_file_contents_free(read2);
  iree_io_file_contents_free(read1);
}

}  // namespace
}  // namespace io
}  // namespace iree

#endif  // IREE_FILE_IO_ENABLE
