Compare commits

..

No commits in common. "46447f8f5a357c3ba7650af3cbc91185b035d445" and "1a7d603a647033b889c73aed6d00cf74426f4489" have entirely different histories.

11 changed files with 163 additions and 371 deletions

View File

@ -31,6 +31,12 @@ def find_device_property(device: Device, name: str) -> DeviceProperty:
return property
return None
def find_binding_property(device: Device, name: str) -> BindingProperty:
for property in device.properties:
if property.name == name:
return property
return None
def find_device_binding(device: Device, bindings: list[Binding]) -> Binding:
compatible_property = find_device_property(device, "compatible")
if compatible_property is None:
@ -58,7 +64,7 @@ def property_to_string(property: DeviceProperty) -> str:
raise Exception(f"property_to_string() has an unsupported type: {type}")
def resolve_parameters_from_bindings(device: Device, bindings: list[Binding]) -> list:
compatible_property = find_device_property(device, "compatible")
compatible_property = find_binding_property(device, "compatible")
if compatible_property is None:
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
device_binding = find_binding(compatible_property.value, bindings)
@ -102,7 +108,7 @@ def write_device_structs(file, device: Device, parent_device: Device, bindings:
print(f"Writing device struct for '{device.identifier}'")
# Assemble some pre-requisites
type_name = get_device_type_name(device, bindings)
compatible_property = find_device_property(device, "compatible")
compatible_property = find_binding_property(device, "compatible")
if compatible_property is None:
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
identifier = get_device_identifier_safe(device)
@ -117,7 +123,7 @@ def write_device_structs(file, device: Device, parent_device: Device, bindings:
# Write device struct
file.write(f"static struct Device {identifier}" " = {\n")
file.write(f"\t.name = \"{device.identifier}\",\n") # Use original name
file.write(f"\t.config = &{config_variable_name},\n")
file.write(f"\t.config = (void*)&{config_variable_name},\n")
file.write(f"\t.parent = {parent_value},\n")
file.write("};\n\n")
# Child devices
@ -128,7 +134,7 @@ def write_device_init(file, device: Device, bindings: list[Binding], verbose: bo
if verbose:
print(f"Processing device init code for '{device.identifier}'")
# Assemble some pre-requisites
compatible_property = find_device_property(device, "compatible")
compatible_property = find_binding_property(device, "compatible")
if compatible_property is None:
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
# Type & instance names
@ -160,7 +166,7 @@ def generate_devicetree_c(filename: str, items: list[object], bindings: list[Bin
#define TAG LOG_TAG(devicetree)
static int init_builtin_device(struct Device* device, const char* compatible) {
struct Driver* driver = driver_find_compatible(compatible);
struct Driver* driver = driver_find(compatible);
if (driver == NULL) {
LOG_E(TAG, "Can't find driver: %s", compatible);
return -1;

View File

@ -29,7 +29,7 @@ static bool set_options(Device* device, gpio_pin_t pin, gpio_flags_t options) {
}
gpio_mode_t mode;
if ((options & GPIO_DIRECTION_INPUT_OUTPUT) == GPIO_DIRECTION_INPUT_OUTPUT) {
if (options & (GPIO_DIRECTION_INPUT_OUTPUT)) {
mode = GPIO_MODE_INPUT_OUTPUT;
} else if (options & GPIO_DIRECTION_INPUT) {
mode = GPIO_MODE_INPUT;
@ -77,6 +77,10 @@ static bool get_options(Device* device, gpio_pin_t pin, gpio_flags_t* options) {
output |= GPIO_DIRECTION_OUTPUT;
}
if (esp_config.oe) {
output |= GPIO_DIRECTION_OUTPUT;
}
if (esp_config.oe_inv) {
output |= GPIO_ACTIVE_LOW;
}

View File

@ -25,7 +25,7 @@ struct Device {
/** The name of the device. Valid characters: a-z a-Z 0-9 - _ . */
const char* name;
/** The configuration data for the device's driver */
const void* config;
void* config;
/** The parent device that this device belongs to. Can be NULL, but only the root device should have a NULL parent. */
struct Device* parent;
/** Internal data */
@ -51,9 +51,8 @@ struct Device {
* Initialize the properties of a device.
*
* @param[in] dev a device with all non-internal properties set
* @return the result code (0 for success)
*/
int device_construct(struct Device* device);
void device_construct(struct Device* device);
/**
* Deinitialize the properties of a device.
@ -81,9 +80,8 @@ static inline bool device_is_ready(const struct Device* device) {
* - a bus (if any)
*
* @param[in] device non-null device pointer
* @return 0 on success
*/
int device_add(struct Device* device);
void device_add(struct Device* device);
/**
* Deregister a device. Remove it from all relevant systems:
@ -92,9 +90,9 @@ int device_add(struct Device* device);
* - a bus (if any)
*
* @param[in] device non-null device pointer
* @return 0 on success
* @return true when the device was found and deregistered
*/
int device_remove(struct Device* device);
bool device_remove(struct Device* device);
/**
* Attach the driver.

View File

@ -4,8 +4,6 @@
extern "C" {
#endif
#include <stdbool.h>
struct Device;
struct DeviceType;
@ -13,7 +11,7 @@ struct Driver {
/** The driver name */
const char* name;
/** Array of const char*, terminated by NULL */
const char**compatible;
const char** compatible;
/** Function to initialize the driver for a device */
int (*start_device)(struct Device* dev);
/** Function to deinitialize the driver for a device */
@ -33,14 +31,12 @@ int driver_construct(struct Driver* driver);
int driver_destruct(struct Driver* driver);
struct Driver* driver_find(const char* compatible);
int driver_bind(struct Driver* driver, struct Device* device);
int driver_unbind(struct Driver* driver, struct Device* device);
bool driver_is_compatible(struct Driver* driver, const char* compatible);
struct Driver* driver_find_compatible(const char* compatible);
static inline const struct DeviceType* driver_get_device_type(struct Driver* driver) {
return driver->device_type;
}

View File

@ -1,8 +1,10 @@
#pragma once
#define ERROR_UNDEFINED 1
#define ERROR_INVALID_STATE 2
#define ERROR_INVALID_ARGUMENT 3
#define ERROR_MISSING_PARAMETER 4
#define ERROR_NOT_FOUND 5
#include <errno.h>
#define CUSTOM_ERROR_CODE(x) (-2000 - x)
#define ERROR_UNDEFINED CUSTOM_ERROR_CODE(1)
#define ERROR_INVALID_STATE CUSTOM_ERROR_CODE(2)
#define ERROR_INVALID_PARAMETER CUSTOM_ERROR_CODE(3)
#define ERROR_MISSING_PARAMETER CUSTOM_ERROR_CODE(4)

View File

@ -28,12 +28,7 @@ struct DeviceLedger {
}
};
static DeviceLedger& get_ledger() {
static DeviceLedger ledger;
return ledger;
}
#define ledger get_ledger()
static DeviceLedger ledger;
extern "C" {
@ -42,14 +37,10 @@ extern "C" {
#define get_device_data(device) static_cast<DeviceData*>(device->internal.data)
int device_construct(Device* device) {
void device_construct(Device* device) {
LOG_I(TAG, "construct %s", device->name);
device->internal.data = new(std::nothrow) DeviceData;
if (device->internal.data == nullptr) {
return ENOMEM;
}
device->internal.data = new DeviceData();
mutex_construct(&device->internal.mutex);
return 0;
}
int device_destruct(Device* device) {
@ -72,19 +63,21 @@ static void device_add_child(struct Device* device, struct Device* child) {
static void device_remove_child(struct Device* device, struct Device* child) {
device_lock(device);
auto* parent_data = get_device_data(device);
const auto iterator = std::ranges::find(parent_data->children, child);
const auto iterator = std::ranges::find(parent_data->children, device);
if (iterator != parent_data->children.end()) {
parent_data->children.erase(iterator);
}
device_unlock(device);
}
int device_add(Device* device) {
void device_add(Device* device) {
LOG_I(TAG, "add %s", device->name);
assert(!device->internal.state.started);
// Already added
if (device->internal.state.started || device->internal.state.added) {
return ERROR_INVALID_STATE;
if (device->internal.state.added) {
return;
}
// Add to ledger
@ -99,14 +92,16 @@ int device_add(Device* device) {
}
device->internal.state.added = true;
return 0;
}
int device_remove(Device* device) {
bool device_remove(Device* device) {
LOG_I(TAG, "remove %s", device->name);
if (device->internal.state.started || !device->internal.state.added) {
return ERROR_INVALID_STATE;
assert(!device->internal.state.started);
// Already removed
if (!device->internal.state.added) {
return true;
}
// Remove self from parent's children list
@ -125,7 +120,7 @@ int device_remove(Device* device) {
ledger_unlock();
device->internal.state.added = false;
return 0;
return true;
failed_ledger_lookup:
@ -134,7 +129,7 @@ failed_ledger_lookup:
device_add_child(parent, device);
}
return ERROR_NOT_FOUND;
return false;
}
int device_start(Device* device) {
@ -142,19 +137,23 @@ int device_start(Device* device) {
return ERROR_INVALID_STATE;
}
if (device->internal.driver == nullptr) {
return ERROR_INVALID_STATE;
}
// Already started
if (device->internal.state.started) {
return 0;
}
if (device->internal.driver == nullptr) {
LOG_E(TAG, "start error: no driver for %s", device->name);
return ERROR_INVALID_STATE;
}
int result = driver_bind(device->internal.driver, device);
device->internal.state.started = (result == 0);
device->internal.state.start_result = result;
return result;
if (result != 0) {
device->internal.state.started = true;
device->internal.state.start_result = result;
}
return 0;
}
int device_stop(struct Device* device) {
@ -162,16 +161,11 @@ int device_stop(struct Device* device) {
return ERROR_INVALID_STATE;
}
// Not started
// Already stopped
if (!device->internal.state.started) {
return 0;
}
int result = driver_unbind(device->internal.driver, device);
if (result != 0) {
return result;
}
device->internal.state.started = false;
device->internal.state.start_result = 0;
return 0;

View File

@ -24,7 +24,7 @@ struct DriverInternalData {
};
struct DriverLedger {
std::vector<Driver*> drivers;
std::vector<Driver*> drivers = {};
Mutex mutex { 0 };
DriverLedger() {
@ -34,45 +34,35 @@ struct DriverLedger {
~DriverLedger() {
mutex_destruct(&mutex);
}
void lock() {
mutex_lock(&mutex);
}
void unlock() {
mutex_unlock(&mutex);
}
};
static DriverLedger& get_ledger() {
static DriverLedger ledger;
return ledger;
}
static DriverLedger ledger;
#define ledger get_ledger()
#define ledger_lock() mutex_lock(&ledger.mutex);
#define ledger_unlock() mutex_unlock(&ledger.mutex);
#define driver_internal_data(driver) static_cast<DriverInternalData*>(driver->internal.data)
#define driver_lock(driver) mutex_lock(&driver_internal_data(driver)->mutex);
#define driver_unlock(driver) mutex_unlock(&driver_internal_data(driver)->mutex);
static void driver_add(Driver* driver) {
LOG_I(TAG, "add %s", driver->name);
ledger.lock();
ledger.drivers.push_back(driver);
ledger.unlock();
static void driver_add(Driver* dev) {
LOG_I(TAG, "add %s", dev->name);
ledger_lock();
ledger.drivers.push_back(dev);
ledger_unlock();
}
static bool driver_remove(Driver* driver) {
LOG_I(TAG, "remove %s", driver->name);
static bool driver_remove(Driver* dev) {
LOG_I(TAG, "remove %s", dev->name);
ledger.lock();
const auto iterator = std::ranges::find(ledger.drivers, driver);
ledger_lock();
const auto iterator = std::ranges::find(ledger.drivers, dev);
// check that there actually is a 3 in our vector
if (iterator == ledger.drivers.end()) {
return false;
}
ledger.drivers.erase(iterator);
ledger.unlock();
ledger_unlock();
return true;
}
@ -80,51 +70,38 @@ static bool driver_remove(Driver* driver) {
extern "C" {
int driver_construct(Driver* driver) {
driver->internal.data = new(std::nothrow) DriverInternalData;
if (driver->internal.data == nullptr) {
return ENOMEM;
}
driver->internal.data = new DriverInternalData();
driver_add(driver);
return 0;
}
int driver_destruct(Driver* driver) {
// Check if in use
if (driver_internal_data(driver)->use_count != 0) {
if (driver_internal_data(driver)->use_count == 0) {
return ERROR_INVALID_STATE;
}
driver_remove(driver);
delete driver_internal_data(driver);
driver->internal.data = nullptr;
return 0;
}
bool driver_is_compatible(Driver* driver, const char* compatible) {
if (compatible == nullptr || driver->compatible == nullptr) {
Driver* driver_find(const char* compatible) {
ledger_lock();
const auto it = std::ranges::find_if(ledger.drivers, [compatible](Driver* driver) {
const char** current_compatible = driver->compatible;
assert(current_compatible != nullptr);
while (*current_compatible != nullptr) {
if (strcmp(compatible, *current_compatible) == 0) {
return true;
}
current_compatible++;
}
return false;
}
const char** compatible_iterator = driver->compatible;
while (*compatible_iterator != nullptr) {
if (strcmp(*compatible_iterator, compatible) == 0) {
return true;
}
compatible_iterator++;
}
return false;
}
Driver* driver_find_compatible(const char* compatible) {
ledger.lock();
Driver* result = nullptr;
for (auto* driver : ledger.drivers) {
if (driver_is_compatible(driver, compatible)) {
result = driver;
break;
}
}
ledger.unlock();
return result;
});
auto* driver = (it != ledger.drivers.end()) ? *it : nullptr;
ledger_unlock();
return driver;
}
int driver_bind(Driver* driver, Device* device) {
@ -132,10 +109,12 @@ int driver_bind(Driver* driver, Device* device) {
int err = 0;
if (!device_is_added(device)) {
err = ERROR_INVALID_STATE;
err = -ENODEV;
goto error;
}
device_set_driver(device, driver);
if (driver->start_device != nullptr) {
err = driver->start_device(device);
if (err != 0) {
@ -158,19 +137,16 @@ error:
int driver_unbind(Driver* driver, Device* device) {
driver_lock(driver);
int err = 0;
if (!device_is_added(device)) {
err = ERROR_INVALID_STATE;
if (driver->stop_device == nullptr) {
return 0;
}
int err = driver->stop_device(device);
if (err != 0) {
goto error;
}
if (driver->stop_device != nullptr) {
err = driver->stop_device(device);
if (err != 0) {
goto error;
}
}
device_set_driver(device, nullptr);
driver_internal_data(driver)->use_count--;
driver_unlock(driver);

View File

@ -8,14 +8,12 @@
TEST_CASE("device_construct and device_destruct should set and unset the correct fields") {
Device device = { 0 };
int error = device_construct(&device);
CHECK_EQ(error, 0);
device_construct(&device);
CHECK_NE(device.internal.data, nullptr);
CHECK_NE(device.internal.mutex.handle, nullptr);
error = device_destruct(&device);
CHECK_EQ(error, 0);
device_destruct(&device);
CHECK_EQ(device.internal.data, nullptr);
CHECK_EQ(device.internal.mutex.handle, nullptr);
@ -25,13 +23,13 @@ TEST_CASE("device_construct and device_destruct should set and unset the correct
comparison_device.internal.mutex.handle = device.internal.mutex.handle;
// Check that no other data was set
CHECK_EQ(memcmp(&device, &comparison_device, sizeof(Device)), 0);
CHECK_EQ(memcmp(&device, &comparison_device, sizeof(struct Device)), 0);
}
TEST_CASE("device_add should add the device to the list of all devices") {
TEST_CASE("device_add should make the device discoverable") {
Device device = { 0 };
CHECK_EQ(device_construct(&device), 0);
CHECK_EQ(device_add(&device), 0);
device_construct(&device);
device_add(&device);
// Gather all devices
std::vector<Device*> devices;
@ -44,8 +42,8 @@ TEST_CASE("device_add should add the device to the list of all devices") {
CHECK_EQ(devices.size(), 1);
CHECK_EQ(devices[0], &device);
CHECK_EQ(device_remove(&device), 0);
CHECK_EQ(device_destruct(&device), 0);
device_remove(&device);
device_destruct(&device);
}
TEST_CASE("device_add should add the device to its parent") {
@ -57,11 +55,11 @@ TEST_CASE("device_add should add the device to its parent") {
.parent = &parent
};
CHECK_EQ(device_construct(&parent), 0);
CHECK_EQ(device_add(&parent), 0);
device_construct(&parent);
device_add(&parent);
CHECK_EQ(device_construct(&child), 0);
CHECK_EQ(device_add(&child), 0);
device_construct(&child);
device_add(&child);
// Gather all child devices
std::vector<Device*> children;
@ -74,117 +72,21 @@ TEST_CASE("device_add should add the device to its parent") {
CHECK_EQ(children.size(), 1);
CHECK_EQ(children[0], &child);
CHECK_EQ(device_remove(&child), 0);
CHECK_EQ(device_destruct(&child), 0);
device_remove(&child);
device_destruct(&child);
CHECK_EQ(device_remove(&parent), 0);
CHECK_EQ(device_destruct(&parent), 0);
device_remove(&parent);
device_destruct(&parent);
}
TEST_CASE("device_add should set the state to 'added'") {
Device device = { 0 };
CHECK_EQ(device_construct(&device), 0);
CHECK_EQ(device.internal.state.added, false);
CHECK_EQ(device_add(&device), 0);
CHECK_EQ(device.internal.state.added, true);
CHECK_EQ(device_remove(&device), 0);
CHECK_EQ(device_destruct(&device), 0);
}
TEST_CASE("device_remove should remove it from the list of all devices") {
Device device = { 0 };
CHECK_EQ(device_construct(&device), 0);
CHECK_EQ(device_add(&device), 0);
CHECK_EQ(device_remove(&device), 0);
// Gather all devices
std::vector<Device*> devices;
for_each_device(&devices, [](auto* device, auto* context) {
auto* devices_ptr = (std::vector<Device*>*)context;
devices_ptr->push_back(device);
return true;
});
CHECK_EQ(devices.size(), 0);
CHECK_EQ(device_destruct(&device), 0);
}
TEST_CASE("device_remove should remove the device from its parent") {
Device parent = { 0 };
Device child = {
.name = nullptr,
.config = nullptr,
.parent = &parent
};
CHECK_EQ(device_construct(&parent), 0);
CHECK_EQ(device_add(&parent), 0);
CHECK_EQ(device_construct(&child), 0);
CHECK_EQ(device_add(&child), 0);
CHECK_EQ(device_remove(&child), 0);
// Gather all child devices
std::vector<Device*> children;
for_each_device_child(&parent, &children, [](auto* child_device, auto* context) {
auto* children_ptr = (std::vector<Device*>*)context;
children_ptr->push_back(child_device);
return true;
});
CHECK_EQ(children.size(), 0);
CHECK_EQ(device_destruct(&child), 0);
CHECK_EQ(device_remove(&parent), 0);
CHECK_EQ(device_destruct(&parent), 0);
}
TEST_CASE("device_remove should clear the state 'added'") {
Device device = { 0 };
device_construct(&device);
CHECK_EQ(device.internal.state.added, false);
device_add(&device);
CHECK_EQ(device.internal.state.added, true);
device_remove(&device);
CHECK_EQ(device.internal.state.added, false);
device_remove(&device);
device_destruct(&device);
}
TEST_CASE("device_is_ready should return true only when it is started") {
static Driver driver = {
.name = "test_driver",
.compatible = (const char*[]) { "test_compatible", nullptr },
.start_device = nullptr,
.stop_device = nullptr,
.api = nullptr,
.device_type = nullptr,
.internal = { 0 }
};
Device device = { 0 };
CHECK_EQ(driver_construct(&driver), 0);
CHECK_EQ(device_construct(&device), 0);
CHECK_EQ(device.internal.state.started, false);
device_set_driver(&device, &driver);
CHECK_EQ(device.internal.state.started, false);
CHECK_EQ(device_add(&device), 0);
CHECK_EQ(device.internal.state.started, false);
int result = device_start(&device);
CHECK_EQ(result, 0);
CHECK_EQ(device.internal.state.started, true);
CHECK_EQ(device_stop(&device), 0);
CHECK_EQ(device.internal.state.started, false);
CHECK_EQ(device_remove(&device), 0);
CHECK_EQ(device.internal.state.started, false);
CHECK_EQ(driver_destruct(&driver), 0);
CHECK_EQ(device_destruct(&device), 0);
}

View File

@ -1,64 +0,0 @@
#include "doctest.h"
#include <Tactility/Device.h>
#include <Tactility/Driver.h>
struct IntegrationDriverConfig {
int startResult;
int stopResult;
};
static int startCalled = 0;
static int stopCalled = 0;
#define integration_data(device) static_cast<IntegrationDriverData*>(device_get_driver_data(device))
#define integration_config(device) static_cast<const IntegrationDriverConfig*>(device->config)
static int start(Device* device) {
startCalled++;
return integration_config(device)->startResult;
}
static int stop(Device* device) {
stopCalled++;
return integration_config(device)->stopResult;
}
static Driver integration_driver = {
.name = "integration_test_driver",
.compatible = (const char*[]) { "integration", nullptr },
.start_device = start,
.stop_device = stop,
.api = nullptr,
.device_type = nullptr,
.internal = { 0 }
};
TEST_CASE("driver with with start success and stop success should start and stop a device") {
startCalled = 0;
stopCalled = 0;
static const IntegrationDriverConfig config {
.startResult = 0,
.stopResult = 0
};
static Device integration_device {
.name = "integration_device",
.config = &config,
.parent = nullptr,
};
CHECK_EQ(driver_construct(&integration_driver), 0);
CHECK_EQ(device_construct(&integration_device), 0);
device_add(&integration_device);
CHECK_EQ(startCalled, 0);
CHECK_EQ(driver_bind(&integration_driver, &integration_device), 0);
CHECK_EQ(startCalled, 1);
CHECK_EQ(stopCalled, 0);
CHECK_EQ(driver_unbind(&integration_driver, &integration_device), 0);
CHECK_EQ(stopCalled, 1);
CHECK_EQ(device_remove(&integration_device), 0);
CHECK_EQ(device_destruct(&integration_device), 0);
CHECK_EQ(driver_destruct(&integration_driver), 0);
}

View File

@ -1,70 +1,6 @@
#include "doctest.h"
#include <Tactility/Driver.h>
TEST_CASE("driver_construct and driver_destruct should set and unset the correct fields") {
Driver driver = { 0 };
int error = driver_construct(&driver);
CHECK_EQ(error, 0);
CHECK_NE(driver.internal.data, nullptr);
error = driver_destruct(&driver);
CHECK_EQ(error, 0);
CHECK_EQ(driver.internal.data, nullptr);
}
TEST_CASE("driver_is_comptable should return true if a compatible value is found") {
Driver driver = {
.name = "test_driver",
.compatible = (const char*[]) { "test_compatible", nullptr },
.start_device = nullptr,
.stop_device = nullptr,
.api = nullptr,
.device_type = nullptr,
.internal = { 0 }
};
CHECK_EQ(driver_is_compatible(&driver, "test_compatible"), true);
CHECK_EQ(driver_is_compatible(&driver, "nope"), false);
CHECK_EQ(driver_is_compatible(&driver, nullptr), false);
}
TEST_CASE("driver_is_comptable should return true if a compatible value is found") {
Driver driver = {
.name = "test_driver",
.compatible = nullptr,
.start_device = nullptr,
.stop_device = nullptr,
.api = nullptr,
.device_type = nullptr,
.internal = { 0 }
};
CHECK_EQ(driver_is_compatible(&driver, nullptr), false);
}
TEST_CASE("driver_find should only find a compatible driver when the driver was constructed") {
// Must be static or outside of function to prevent SIGSEV crash due to memory corruption while iterating .compatible
static Driver driver = {
.name = "test_driver",
.compatible = (const char*[]) { "test_compatible", nullptr },
.start_device = nullptr,
.stop_device = nullptr,
.api = nullptr,
.device_type = nullptr,
.internal = { 0 }
};
Driver* found_driver = driver_find_compatible("test_compatible");
CHECK_EQ(found_driver, nullptr);
int error = driver_construct(&driver);
CHECK_EQ(error, 0);
found_driver = driver_find_compatible("test_compatible");
CHECK_EQ(found_driver, &driver);
error = driver_destruct(&driver);
CHECK_EQ(error, 0);
found_driver = driver_find_compatible("test_compatible");
CHECK_EQ(found_driver, nullptr);
TEST_CASE("placeholder driver test") {
// TODO: Implement
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "doctest.h"
#include <unistd.h>
#include <Tactility/file/File.h>
/**
* A class for creating test files that can automatically clean themselves up.
*/
class TestFile {
const char* path;
bool autoClean;
public:
TestFile(const char* path, bool autoClean = true) : path(path), autoClean(autoClean) {
if (autoClean && exists()) {
remove();
}
}
~TestFile() {
if (autoClean && exists()) {
remove();
}
}
const char* getPath() const { return path; }
void writeData(const char* data) const {
CHECK_EQ(tt::file::writeString(path, data), true);
}
bool exists() const {
return access(path, F_OK) == 0;
}
void remove() const {
::remove(path);
}
};