From bce9ab296cac62bebfa91abe089cb5e824a8b8d9 Mon Sep 17 00:00:00 2001 From: starkos Date: Tue, 27 May 2008 18:47:25 +0000 Subject: [PATCH] Added support for wildcard matching (r391:396) --- src/action/make/gmake_project.c | 2 +- src/action/make/make_project.c | 22 ++-- src/action/make/tests/gmake_project_tests.cpp | 2 +- src/base/cstr.c | 12 +++ src/base/cstr.h | 1 + src/base/string.c | 10 +- src/base/string.h | 8 +- src/base/tests/cstr_tests.cpp | 15 +++ src/base/tests/string_tests.cpp | 2 +- src/platform/platform.h | 33 ++++++ src/platform/posix.c | 85 +++++++++++++++ src/platform/windows.c | 58 ++++++++++ src/project/fields.h | 5 +- src/project/project.c | 2 +- src/script/fn_accessor.c | 58 +++++----- src/script/fn_dofile.c | 4 +- src/script/fn_match.c | 101 ++++++++++++++++++ src/script/script.c | 7 +- src/script/script_internal.c | 2 +- src/script/script_internal.h | 1 + src/script/tests/fn_accessor_tests.cpp | 13 +++ src/script/tests/fn_match_tests.cpp | 58 ++++++++++ 22 files changed, 447 insertions(+), 54 deletions(-) create mode 100644 src/script/fn_match.c create mode 100644 src/script/tests/fn_match_tests.cpp diff --git a/src/action/make/gmake_project.c b/src/action/make/gmake_project.c index f147b227..adb56604 100644 --- a/src/action/make/gmake_project.c +++ b/src/action/make/gmake_project.c @@ -28,7 +28,7 @@ int gmake_project_shell_detect(Session sess, Project prj, Stream strm) z |= stream_writeline(strm, "ifeq (,$(ComSpec)$(COMSPEC))"); z |= stream_writeline(strm, " SHELLTYPE := posix"); z |= stream_writeline(strm, "endif"); - z |= stream_writeline(strm, "ifeq (/bin/sh.exe,$(SHELL))"); + z |= stream_writeline(strm, "ifeq (/bin,$(findstring /bin,$(SHELL)))"); z |= stream_writeline(strm, " SHELLTYPE := posix"); z |= stream_writeline(strm, "endif"); z |= stream_writeline(strm, ""); diff --git a/src/action/make/make_project.c b/src/action/make/make_project.c index df8cbab0..8487e55e 100644 --- a/src/action/make/make_project.c +++ b/src/action/make/make_project.c @@ -223,8 +223,11 @@ int make_project_objects(Session sess, Project prj, Stream strm) for (i = 0; i < n; ++i) { const char* filename = strings_item(files, i); - const char* obj_name = make_get_obj_filename(filename); - z |= stream_writeline(strm, "\t%s \\", obj_name); + if (path_is_cpp_source(filename)) + { + const char* obj_name = make_get_obj_filename(filename); + z |= stream_writeline(strm, "\t%s \\", obj_name); + } } z |= stream_writeline(strm, ""); @@ -288,11 +291,14 @@ int make_project_source_rules(Session sess, Project prj, Stream strm) for (i = 0; i < n; ++i) { const char* filename = strings_item(files, i); - const char* obj_name = make_get_obj_filename(filename); - z |= stream_writeline(strm, "%s: %s", obj_name, filename); - z |= stream_writeline(strm, "\t@echo $(notdir $<)"); - z |= stream_writeline(strm, "\t@$(CXX) $(CXXFLAGS) -o $@ -c $<"); - z |= stream_writeline(strm, ""); + if (path_is_cpp_source(filename)) + { + const char* obj_name = make_get_obj_filename(filename); + z |= stream_writeline(strm, "%s: %s", obj_name, filename); + z |= stream_writeline(strm, "\t@echo $(notdir $<)"); + z |= stream_writeline(strm, "\t@$(CXX) $(CXXFLAGS) -o $@ -c $<"); + z |= stream_writeline(strm, ""); + } } return z; @@ -308,7 +314,7 @@ int make_project_target(Session sess, Project prj, Stream strm) UNUSED(sess); z |= stream_writeline(strm, "$(OUTFILE): $(OUTDIR) $(OBJDIR) $(OBJECTS) $(LDDEPS) $(RESOURCES)"); z |= stream_writeline(strm, "\t@echo Linking %s", project_get_name(prj)); - z |= stream_writeline(strm, "\t$(CXX) -o $@ $(LDFLAGS) $(ARCHFLAGS) $(OBJECTS) $(RESOURCES)"); + z |= stream_writeline(strm, "\t@$(CXX) -o $@ $(LDFLAGS) $(ARCHFLAGS) $(OBJECTS) $(RESOURCES)"); z |= stream_writeline(strm, ""); return z; } diff --git a/src/action/make/tests/gmake_project_tests.cpp b/src/action/make/tests/gmake_project_tests.cpp index e14f3eb9..55646d32 100644 --- a/src/action/make/tests/gmake_project_tests.cpp +++ b/src/action/make/tests/gmake_project_tests.cpp @@ -169,7 +169,7 @@ SUITE(action) CHECK_EQUAL( "$(OUTFILE): $(OUTDIR) $(OBJDIR) $(OBJECTS) $(LDDEPS) $(RESOURCES)\n" "\t@echo Linking MyProject\n" - "\t$(CXX) -o $@ $(LDFLAGS) $(ARCHFLAGS) $(OBJECTS) $(RESOURCES)\n" + "\t@$(CXX) -o $@ $(LDFLAGS) $(ARCHFLAGS) $(OBJECTS) $(RESOURCES)\n" "\n", buffer); } diff --git a/src/base/cstr.c b/src/base/cstr.c index 898a5692..517e8fe2 100644 --- a/src/base/cstr.c +++ b/src/base/cstr.c @@ -13,6 +13,18 @@ #include "base/cstr.h" +/** + * Determines if the sequence appears anywhere in the target string. + * \param str The string to test. + * \param expected The sequence to search for. + * \returns True if the sequence is contained in the string. + */ +int cstr_contains(const char* str, const char* expected) +{ + return (strstr(str, expected) != NULL); +} + + /** * Determines if the string ends with a particular sequence. * \param str The string to test. diff --git a/src/base/cstr.h b/src/base/cstr.h index f193becf..2d6f0327 100644 --- a/src/base/cstr.h +++ b/src/base/cstr.h @@ -13,6 +13,7 @@ #if !defined(PREMAKE_CSTR_H) #define PREMAKE_CSTR_H +int cstr_contains(const char* str, const char* expected); int cstr_ends_with(const char* str, const char* expected); int cstr_eq(const char* str, const char* expected); int cstr_eqi(const char* str, const char* expected); diff --git a/src/base/string.c b/src/base/string.c index 5111a131..5868b1f8 100644 --- a/src/base/string.c +++ b/src/base/string.c @@ -10,7 +10,7 @@ #include "base/string.h" -DEFINE_CLASS(string) +DEFINE_CLASS(String) { char* contents; int capacity; @@ -22,11 +22,11 @@ DEFINE_CLASS(string) * \param value The C string value. * \returns A new dynamic string object containing a copy of the string. */ -string string_create(const char* value) +String string_create(const char* value) { if (value != NULL) { - string str = ALLOC_CLASS(string); + String str = ALLOC_CLASS(String); str->capacity = strlen(value) + 1; str->contents = (char*)malloc(str->capacity); strcpy(str->contents, value); @@ -43,7 +43,7 @@ string string_create(const char* value) * Destroy a dynamic string object and free the associated memory. * \param str The string to destroy. */ -void string_destroy(string str) +void string_destroy(String str) { if (str != NULL) { @@ -58,7 +58,7 @@ void string_destroy(string str) * \param str The string to query. * \returns The C string value. */ -const char* string_cstr(string str) +const char* string_cstr(String str) { if (str != NULL) return str->contents; diff --git a/src/base/string.h b/src/base/string.h index 29ecea5f..8b8ba332 100644 --- a/src/base/string.h +++ b/src/base/string.h @@ -13,11 +13,11 @@ #if !defined(PREMAKE_STRING_H) #define PREMAKE_STRING_H -DECLARE_CLASS(string); +DECLARE_CLASS(String); -string string_create(const char* value); -void string_destroy(string str); -const char* string_cstr(string str); +String string_create(const char* value); +void string_destroy(String str); +const char* string_cstr(String str); #endif /** @} */ diff --git a/src/base/tests/cstr_tests.cpp b/src/base/tests/cstr_tests.cpp index 1302fc44..3a3f4566 100644 --- a/src/base/tests/cstr_tests.cpp +++ b/src/base/tests/cstr_tests.cpp @@ -12,6 +12,21 @@ extern "C" { SUITE(cstr) { + /************************************************************************** + * cstr_contains() tests + **************************************************************************/ + + TEST(CStrContains_ReturnsTrue_OnMatch) + { + CHECK(cstr_contains("Abcdef", "cd")); + } + + TEST(CStrContains_ReturnsFalse_OnMismatch) + { + CHECK(!cstr_contains("Abcdef", "xy")); + } + + /************************************************************************** * cstr_ends_with() tests **************************************************************************/ diff --git a/src/base/tests/string_tests.cpp b/src/base/tests/string_tests.cpp index 5cb2617f..6ddd304d 100644 --- a/src/base/tests/string_tests.cpp +++ b/src/base/tests/string_tests.cpp @@ -18,7 +18,7 @@ SUITE(base) TEST(StringCreate_ReturnsNull_OnNull) { - string str = string_create(NULL); + String str = string_create(NULL); CHECK(str == NULL); } diff --git a/src/platform/platform.h b/src/platform/platform.h index 4d0fbaba..64eccc0f 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -38,6 +38,8 @@ enum Platform #define PLATFORM_WINDOWS (1) #endif +DECLARE_CLASS(PlatformSearch) + /** * Create a directory, if it doesn't exist already. @@ -76,6 +78,37 @@ int platform_dir_set_current(const char* path); enum Platform platform_get(void); +/** + * Create a new platform file search context. + */ +PlatformSearch platform_search_create(const char* mask); + + +/** + * Destroy a platform search context. + */ +void platform_search_destroy(PlatformSearch search); + + +/** + * Retrieve the name of the current match in the search. + */ +const char* platform_search_get_name(PlatformSearch search); + + +/** + * Determine if the current match is a file or a directory. + */ +int platform_search_is_file(PlatformSearch search); + + +/** + * Retrieve the next match in a file system search. + * \returns True if another match is available. + */ +int platform_search_next(PlatformSearch search); + + /** * Set the platform identification string, forcing a platform-specific * behavior regardless of the actual current platform. diff --git a/src/platform/posix.c b/src/platform/posix.c index 039bf9ec..5f595fe8 100644 --- a/src/platform/posix.c +++ b/src/platform/posix.c @@ -6,6 +6,9 @@ #include "premake.h" #include "platform/platform.h" +#include "base/path.h" +#include "base/string.h" + #if !defined(PLATFORM_WINDOWS) #include @@ -18,6 +21,14 @@ #include #include +DEFINE_CLASS(PlatformSearch) +{ + String directory; + String mask; + DIR* handle; + struct dirent* entry; +}; + int platform_create_dir(const char* path) { @@ -47,5 +58,79 @@ int platform_dir_set_current(const char* path) } +PlatformSearch platform_search_create(const char* mask) +{ + PlatformSearch search; + const char* dir; + + dir = path_directory(mask); + mask = path_filename(mask); + if (strlen(dir) == 0) + { + dir = "."; + } + + search = ALLOC_CLASS(PlatformSearch); + search->directory = string_create(dir); + search->mask = string_create(mask); + search->handle = opendir(dir); + search->entry = NULL; + return search; +} + + +void platform_search_destroy(PlatformSearch search) +{ + if (search->handle != NULL) + { + closedir(search->handle); + } + free(search); +} + + +const char* platform_search_get_name(PlatformSearch search) +{ + return search->entry->d_name; +} + + +int platform_search_is_file(PlatformSearch search) +{ + struct stat info; + + const char* dir = string_cstr(search->directory); + const char* path = path_join(dir, search->entry->d_name); + if (stat(path, &info) == 0) + { + return S_ISREG(info.st_mode); + } + + return 0; +} + + +int platform_search_next(PlatformSearch search) +{ + const char* mask = string_cstr(search->mask); + + if (search->handle == NULL) + { + return 0; + } + + search->entry = readdir(search->handle); + while (search->entry != NULL) + { + if (fnmatch(mask, search->entry->d_name, 0) == 0) + { + return 1; + } + search->entry = readdir(search->handle); + } + + return 0; +} + #endif diff --git a/src/platform/windows.c b/src/platform/windows.c index 622b1c86..40de7c63 100644 --- a/src/platform/windows.c +++ b/src/platform/windows.c @@ -4,6 +4,7 @@ * \author Copyright (c) 2002-2008 Jason Perkins and the Premake project */ +#include #include "premake.h" #include "platform/platform.h" #if defined(PLATFORM_WINDOWS) @@ -12,6 +13,14 @@ #include +DEFINE_CLASS(PlatformSearch) +{ + HANDLE handle; + int is_first; + WIN32_FIND_DATA entry; +}; + + int platform_create_dir(const char* path) { return CreateDirectory(path, NULL) ? OKAY : !OKAY; @@ -44,4 +53,53 @@ int platform_dir_set_current(const char* path) } +PlatformSearch platform_search_create(const char* mask) +{ + PlatformSearch search = ALLOC_CLASS(PlatformSearch); + search->handle = FindFirstFile(mask, &search->entry); + search->is_first = 1; + return search; +} + + +void platform_search_destroy(PlatformSearch search) +{ + if (search->handle != INVALID_HANDLE_VALUE) + { + FindClose(search->handle); + } + free(search); +} + + +const char* platform_search_get_name(PlatformSearch search) +{ + return search->entry.cFileName; +} + + +int platform_search_is_file(PlatformSearch search) +{ + return (search->entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; +} + + +int platform_search_next(PlatformSearch search) +{ + if (search->handle == INVALID_HANDLE_VALUE) + { + return 0; + } + + if (search->is_first) + { + search->is_first = 0; + return 1; + } + else + { + return FindNextFile(search->handle, &search->entry); + } +} + #endif diff --git a/src/project/fields.h b/src/project/fields.h index 15835514..63368861 100644 --- a/src/project/fields.h +++ b/src/project/fields.h @@ -18,7 +18,8 @@ enum FieldKind { StringField, - ListField + ListField, + FilesField }; @@ -36,7 +37,7 @@ typedef int (*FieldValidator)(const char* value); struct FieldInfo { const char* name; /**< The name of the field. */ - enum FieldKind kind; /**< StringField or ListField */ + enum FieldKind kind; /**< StringField, ListField, etc. */ FieldValidator validator; /**< The field validation function */ }; diff --git a/src/project/project.c b/src/project/project.c index 30c9a388..097f5f5b 100644 --- a/src/project/project.c +++ b/src/project/project.c @@ -20,7 +20,7 @@ struct FieldInfo ProjectFieldInfo[] = { { "basedir", StringField, NULL }, - { "files", ListField, NULL }, + { "files", FilesField, NULL }, { "guid", StringField, guid_is_valid }, { "language", StringField, project_is_valid_language }, { "location", StringField, NULL }, diff --git a/src/script/fn_accessor.c b/src/script/fn_accessor.c index 01554f0a..840731ed 100644 --- a/src/script/fn_accessor.c +++ b/src/script/fn_accessor.c @@ -16,7 +16,7 @@ static int fn_accessor_register(lua_State* L, struct FieldInfo* fields); static int fn_accessor_register_field(lua_State* L, struct FieldInfo* field); static int fn_accessor_set_string_value(lua_State* L, struct FieldInfo* field); static int fn_accessor_set_list_value(lua_State* L, struct FieldInfo* field); -static void fn_accessor_append_values(lua_State* L, int destIndex, int srcIndex); +static void fn_accessor_append_value(lua_State* L, struct FieldInfo* field, int tbl, int idx); /** @@ -177,17 +177,8 @@ static int fn_accessor_set_list_value(lua_State* L, struct FieldInfo* field) /* get the current value of the field */ lua_getfield(L, -1, field->name); - /* if the value passed in is a table, append all of its contents to the current - * field value. If the value passed in is a single string, add it at the end */ - if (lua_istable(L, 1)) - { - fn_accessor_append_values(L, lua_gettop(L), 1); - } - else - { - lua_pushvalue(L, 1); - lua_rawseti(L, -2, luaL_getn(L, -2) + 1); - } + /* move the values into the field */ + fn_accessor_append_value(L, field, lua_gettop(L), 1); /* remove the field value from the stack */ lua_pop(L, 1); @@ -196,26 +187,43 @@ static int fn_accessor_set_list_value(lua_State* L, struct FieldInfo* field) /** - * Copy values from one table to the end of another table. - * \param destIndex The absolute stack index of the destination table. - * \param srcIndex The absolute stack index of the source table. + * Append a value to table. If the value is itself a table, it is "flattened" into the + * destination table by iterating over each of its items and adding each in turn to the + * target table. + * \param L The current Lua state. + * \param field A description of the field being populated. + * \param tbl The table to contain the values. + * \param idx The value to add to the table. */ -static void fn_accessor_append_values(lua_State* L, int destIndex, int srcIndex) +static void fn_accessor_append_value(lua_State* L, struct FieldInfo* field, int tbl, int idx) { - int i; - int src_n = luaL_getn(L, srcIndex); - int dst_n = luaL_getn(L, destIndex); - for (i = 1; i <= src_n; ++i) + int i, n; + if (lua_istable(L, idx)) { - lua_rawgeti(L, srcIndex, i); - if (lua_istable(L, -1)) + n = luaL_getn(L, idx); + for (i = 1; i <= n; ++i) { - fn_accessor_append_values(L, destIndex, lua_gettop(L)); - dst_n = luaL_getn(L, destIndex); + lua_rawgeti(L, idx, i); + fn_accessor_append_value(L, field, tbl, lua_gettop(L)); + } + lua_pop(L, 1); + } + else + { + /* if this field contains files, check for and expand wildcards by calling match() */ + const char* value = lua_tostring(L, -1); + if (field->kind == FilesField && cstr_contains(value, "*")) + { + lua_getglobal(L, "match"); + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + fn_accessor_append_value(L, field, tbl, lua_gettop(L)); + lua_pop(L, 1); } else { - lua_rawseti(L, destIndex, ++dst_n); + n = luaL_getn(L, tbl); + lua_rawseti(L, tbl, n + 1); } } } diff --git a/src/script/fn_dofile.c b/src/script/fn_dofile.c index f52822c1..f10f324a 100644 --- a/src/script/fn_dofile.c +++ b/src/script/fn_dofile.c @@ -21,8 +21,8 @@ int fn_dofile(lua_State* L) const char *filename; const char* full_path; const char* script_dir; - string old_file; - string old_working_dir; + String old_file; + String old_working_dir; int top, result; filename = luaL_checkstring(L, 1); diff --git a/src/script/fn_match.c b/src/script/fn_match.c new file mode 100644 index 00000000..f05a7a58 --- /dev/null +++ b/src/script/fn_match.c @@ -0,0 +1,101 @@ +/** + * \file fn_match.c + * \brief Perform a wildcard match for files. + * \author Copyright (c) 2008 Jason Perkins and the Premake project + */ + +#include "premake.h" +#include "script_internal.h" +#include "platform/platform.h" +#include "base/cstr.h" +#include "base/path.h" +#include "base/string.h" + +static void do_scan(lua_State* L, const char* mask); + + +/** + * Perform a wildcard match for files; returns a table of file names which + * match the supplied pattern. + */ +int fn_match(lua_State* L) +{ + int i, n; + + /* table to hold the results */ + lua_newtable(L); + + /* scan each mask in the provided list */ + n = lua_gettop(L); + for (i = 1; i < n; ++i) + { + const char* mask = luaL_checkstring(L, i); + do_scan(L, mask); + } + + return 1; +} + + +/** + * Does the real work of scanning the file system and matching the supplied patterns. + */ +void do_scan(lua_State* L, const char* mask) +{ + /* mark the end of the results lists so I know where to add new entries */ + int n = luaL_getn(L, -1); + + /* the search will only return file names; remember the path so I can add it back */ + String dir = string_create(path_directory(mask)); + + /* search */ + PlatformSearch search = platform_search_create(mask); + while (platform_search_next(search)) + { + const char* filename = platform_search_get_name(search); + int is_file = platform_search_is_file(search); + + if (is_file) + { + /* add it to the results */ + const char* path = path_join(string_cstr(dir), filename); + lua_pushstring(L, path); + lua_rawseti(L, -2, ++n); + } + } + platform_search_destroy(search); + + /* if the mask uses the ** pattern, recurse into subdirectories */ + if (cstr_contains(mask, "**")) + { + mask = path_filename(mask); + + /* look for subdirectories */ + search = platform_search_create(path_join(string_cstr(dir), "*")); + while (platform_search_next(search)) + { + if (!platform_search_is_file(search)) + { + const char* dirname = platform_search_get_name(search); + if (dirname[0] != '.') + { + /* build a new mask from the original directory, this new subdirectory, + * and the original search mask. Need to put it in a string to ensure + * its buffer doesn't get overwritten */ + String subsearch; + const char* path = path_join(string_cstr(dir), dirname); + path = path_join(path, mask); + subsearch = string_create(path); + + /* recurse to search this subdirectory */ + do_scan(L, string_cstr(subsearch)); + + string_destroy(subsearch); + } + } + } + platform_search_destroy(search); + } + + string_destroy(dir); +} diff --git a/src/script/script.c b/src/script/script.c index 65d52d3c..d2eb915c 100644 --- a/src/script/script.c +++ b/src/script/script.c @@ -15,9 +15,10 @@ /** Functions to add to the global namespace */ static const luaL_Reg global_funcs[] = { - { "dofile", fn_dofile }, - { "include", fn_include }, - { "project", fn_project }, + { "dofile", fn_dofile }, + { "include", fn_include }, + { "match", fn_match }, + { "project", fn_project }, { "solution", fn_solution }, { NULL, NULL } }; diff --git a/src/script/script_internal.c b/src/script/script_internal.c index 171c78ae..03fc9126 100644 --- a/src/script/script_internal.c +++ b/src/script/script_internal.c @@ -159,7 +159,7 @@ void script_internal_populate_object(lua_State* L, struct FieldInfo* fields) /* set all list-type configuration values to empty tables */ for (field = fields; field->name != NULL; ++field) { - if (field->kind == ListField) + if (field->kind != StringField) { lua_newtable(L); lua_setfield(L, -2, field->name); diff --git a/src/script/script_internal.h b/src/script/script_internal.h index 94ce9bc7..8290b56f 100644 --- a/src/script/script_internal.h +++ b/src/script/script_internal.h @@ -52,6 +52,7 @@ int fn_dofile(lua_State* L); int fn_error(lua_State* L); int fn_getcwd(lua_State* L); int fn_include(lua_State* L); +int fn_match(lua_State* L); int fn_project(lua_State* L); int fn_solution(lua_State* L); diff --git a/src/script/tests/fn_accessor_tests.cpp b/src/script/tests/fn_accessor_tests.cpp index 9143c1e1..d568f773 100644 --- a/src/script/tests/fn_accessor_tests.cpp +++ b/src/script/tests/fn_accessor_tests.cpp @@ -117,4 +117,17 @@ SUITE(script) "return (prj.files[1] == 'Hello.c' and prj.files[2] == 'Goodbye.c')" ); CHECK_EQUAL("true", result); } + + + /************************************************************************** + * List field tests + **************************************************************************/ + + TEST_FIXTURE(FxAccessor, Accessor_ExpandsWildcards) + { + const char* result = script_run_string(script, + "files { 'testing/test_files/*.lua' };" + "return (#prj.files > 0)"); + CHECK_EQUAL("true", result); + } } diff --git a/src/script/tests/fn_match_tests.cpp b/src/script/tests/fn_match_tests.cpp new file mode 100644 index 00000000..50f15d75 --- /dev/null +++ b/src/script/tests/fn_match_tests.cpp @@ -0,0 +1,58 @@ +/** + * \file fn_match_tests.cpp + * \brief Automated test for the match() function. + * \author Copyright (c) 2008 Jason Perkins and the Premake project + */ + +#include "premake.h" +#include "script_tests.h" +extern "C" { +} + +struct FxMatch : FxScript +{ + FxMatch() + { + script_run_string(script, + "function contains(tbl,val)" + " for i,v in ipairs(files) do" + " if (v == val) then return true end" + " end" + " return false;" + "end"); + }; +}; + + +SUITE(script) +{ + TEST_FIXTURE(FxMatch, Match_Exists_OnStartup) + { + const char* result = script_run_string(script, + "return (match ~= nil)"); + CHECK_EQUAL("true", result); + } + + TEST_FIXTURE(FxMatch, Match_ReturnsEmptyTable_OnNoMatches) + { + const char* result = script_run_string(script, + "return #match('*.xyz')"); + CHECK_EQUAL("0", result); + } + + TEST_FIXTURE(FxMatch, Match_ReturnsMatches_OnMatch) + { + const char* result = script_run_string(script, + "files = match('testing/test_files/*.lua');" + "return contains(files, 'testing/test_files/true.lua');"); + CHECK_EQUAL("true", result); + } + + TEST_FIXTURE(FxMatch, Match_Recurses_OnDoubleStar) + { + const char* result = script_run_string(script, + "files = match('testing/test_files/**.lua');" + "return contains(files, 'testing/test_files/nested/getcwd.lua');"); + CHECK_EQUAL("true", result); + } +}