bool from_env_variable,
bool negate,
const std::string& origin);
+
+ friend struct ConfigTester;
};
inline const std::string&
#include "Util.hpp"
+#include "Config.hpp"
#include "FormatNonstdStringView.hpp"
#include "ccache.hpp"
get_cache_files_internal(dir, 1, progress_receiver, files);
}
+std::string
+get_path_in_cache(nonstd::string_view name, nonstd::string_view suffix)
+{
+ std::string path = g_config.cache_dir();
+
+ auto cache_dir_levels = g_config.cache_dir_levels();
+ path.reserve(path.size() + cache_dir_levels * 2 + 1 + name.length()
+ - cache_dir_levels + suffix.length());
+
+ unsigned level = 0;
+ for (; level < cache_dir_levels; ++level) {
+ path.push_back('/');
+ path.push_back(name.at(level));
+ }
+
+ path.push_back('/');
+ string_view name_remaining = name.substr(level);
+ path.append(name_remaining.data(), name_remaining.length());
+ path.append(suffix.data(), suffix.length());
+
+ return path;
+}
+
int
parse_int(const std::string& value)
{
const ProgressReceiver& progress_receiver,
std::vector<std::shared_ptr<CacheFile>>& files);
+// Join the global cache directory, a '/', `name`, and `suffix` into a single
+// path and return it. Additionally N single-character, '/'-separated subpaths
+// are split from the beginning of `name` before joining them all, where N is
+// the number of globally configured cache dir levels.
+//
+// Throws if cache dir levels is greater than the length of `name`.
+//
+// E.g. "ABCDEF" and ".foo" will become "/ccache/A/B/CDEF.foo" when
+// the cache directory is "/ccache" and cache dir levels is 2.
+std::string get_path_in_cache(nonstd::string_view name,
+ nonstd::string_view suffix);
+
// Write bytes in big endian order from an integer value.
//
// Parameters:
char result_name_string[DIGEST_STRING_BUFFER_SIZE];
digest_as_string(result_name, result_name_string);
cached_result_name = result_name;
- cached_result_path = get_path_in_cache(result_name_string, ".result");
+ cached_result_path =
+ x_strdup(Util::get_path_in_cache(result_name_string, ".result").c_str());
stats_file =
format("%s/%c/stats", g_config.cache_dir().c_str(), result_name_string[0]);
}
char manifest_name_string[DIGEST_STRING_BUFFER_SIZE];
hash_result_as_string(hash, manifest_name_string);
- manifest_path = get_path_in_cache(manifest_name_string, ".manifest");
+ manifest_path = x_strdup(
+ Util::get_path_in_cache(manifest_name_string, ".manifest").c_str());
manifest_stats_file = format(
"%s/%c/stats", g_config.cache_dir().c_str(), manifest_name_string[0]);
void fatal(const char* format, ...) ATTR_FORMAT(printf, 1, 2) ATTR_NORETURN;
void warn(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
-char* get_path_in_cache(const char* name, const char* suffix);
bool copy_fd(int fd_in, int fd_out);
bool clone_file(const char* src, const char* dest, bool via_tmp_file);
bool copy_file(const char* src, const char* dest, bool via_tmp_file);
x_exit(1);
}
-// Transform a name to a full path into the cache directory, creating needed
-// sublevels if needed. Caller frees.
-char*
-get_path_in_cache(const char* name, const char* suffix)
-{
- char* path = x_strdup(g_config.cache_dir().c_str());
- for (unsigned i = 0; i < g_config.cache_dir_levels(); ++i) {
- char* p = format("%s/%c", path, name[i]);
- free(path);
- path = p;
- }
-
- char* result =
- format("%s/%s%s", path, name + g_config.cache_dir_levels(), suffix);
- free(path);
- return result;
-}
-
// Copy all data from fd_in to fd_out.
bool
copy_fd(int fd_in, int fd_out)
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#include "test_Config.hpp"
+
#include "../src/Config.hpp"
#include "../src/Error.hpp"
#include "../src/Util.hpp"
--- /dev/null
+#pragma once
+
+#include "../src/Config.hpp"
+
+// Helper to modify private Config variables without adding setters that would
+// only be used for testing.
+// This struct MUST NOT have any data members, only the required private member
+// variables should be exposed.
+struct ConfigTester : Config {
+ using Config::m_cache_dir_levels;
+};
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#include "../src/Config.hpp"
#include "../src/Util.hpp"
+#include "test_Config.hpp"
#include "third_party/catch.hpp"
+using Catch::EndsWith;
using Catch::Equals;
TEST_CASE("Util::base_name")
}
}
+TEST_CASE("Util::get_path_in_cache")
+{
+ Config saved = g_config;
+
+ ConfigTester testconfig;
+ testconfig.set_cache_dir("/zz/ccache");
+
+ {
+ testconfig.m_cache_dir_levels = 0;
+ g_config = testconfig;
+ std::string path = Util::get_path_in_cache("ABCDEF", ".suffix");
+ CHECK(path == "/zz/ccache/ABCDEF.suffix");
+ }
+
+ {
+ testconfig.m_cache_dir_levels = 1;
+ g_config = testconfig;
+ std::string path = Util::get_path_in_cache("ABCDEF", ".suffix");
+ CHECK(path == "/zz/ccache/A/BCDEF.suffix");
+ }
+
+ {
+ testconfig.m_cache_dir_levels = 4;
+ g_config = testconfig;
+ std::string path = Util::get_path_in_cache("ABCDEF", ".suffix");
+ CHECK(path == "/zz/ccache/A/B/C/D/EF.suffix");
+ }
+
+ {
+ testconfig.m_cache_dir_levels = 0;
+ g_config = testconfig;
+ std::string path = Util::get_path_in_cache("", ".suffix");
+ CHECK(path == "/zz/ccache/.suffix");
+ }
+
+ {
+ testconfig.m_cache_dir_levels = 2;
+ g_config = testconfig;
+ std::string path = Util::get_path_in_cache("AB", ".suffix");
+ CHECK(path == "/zz/ccache/A/B/.suffix");
+ }
+
+ {
+ testconfig.m_cache_dir_levels = 3;
+ g_config = testconfig;
+ REQUIRE_THROWS_WITH(Util::get_path_in_cache("AB", ".suffix"),
+ EndsWith("string_view::at()"));
+ }
+
+ g_config = saved;
+}
+
TEST_CASE("Util::int_to_big_endian")
{
uint8_t bytes[8];