Skip to content

Commit

Permalink
- use local cache version using semver syntax.
Browse files Browse the repository at this point in the history
- add tests to ensure local cache works as supposed to be.
  • Loading branch information
lukka committed Dec 19, 2022
1 parent f406847 commit de0f046
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 71 deletions.
5 changes: 3 additions & 2 deletions __tests__/action_failed.test.ts
Expand Up @@ -30,8 +30,9 @@ var coreError = jest.spyOn(core, 'error');
var toolsCacheDir = jest.spyOn(toolcache, 'cacheDir');

test('testing get-cmake action failure', async () => {
process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${process.pid}`);
process.env.RUNNER_TOOL_CACHE = path.join(os.tmpdir(), `${process.pid}-cache`);
const testId = Math.random();
process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${testId}`);
process.env.RUNNER_TOOL_CACHE = path.join(os.tmpdir(), `${testId}-cache`);
await getcmake.main();
expect(coreSetFailed).toBeCalledTimes(1);
expect(coreError).toBeCalledTimes(0);
Expand Down
56 changes: 49 additions & 7 deletions __tests__/action_succeeded.localcloud.test.ts
Expand Up @@ -43,22 +43,27 @@ var toolsCacheDir = jest.spyOn(toolcache, 'cacheDir');
var toolsFind = jest.spyOn(toolcache, 'find');

test('testing get-cmake action success with cloud/local cache enabled', async () => {
const testId = Math.random();
process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${testId}`);
process.env.RUNNER_TOOL_CACHE = path.join(os.tmpdir(), `${testId}-cache`);

for (var matrix of [
{ version: "latest", cloudCache: "true", localCache: "true" },
{ version: "latest", cloudCache: "true", localCache: "false" },
{ version: "latest", cloudCache: "false", localCache: "true" },
{ version: "latest", cloudCache: "false", localCache: "false" }]) {

process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${process.pid}`);
process.env.RUNNER_TOOL_CACHE = path.join(os.tmpdir(), `${process.pid}-cache`);
console.log(`\n\ntesting for: ${JSON.stringify(matrix)}:\n`)

process.env["CUSTOM_CMAKE_VERSION"] = matrix.version;
process.env[localCacheInput] = matrix.localCache;
process.env[cloudCacheInput] = matrix.cloudCache;
await main();
expect(coreSetFailed).toBeCalledTimes(0);
expect(coreError).toBeCalledTimes(0);
expect(toolsCacheDir).toBeCalledTimes(matrix.localCache === "true" ? 1 : 0);
expect(toolsFind).toBeCalledTimes(matrix.localCache === "true" ? 1 : 0);
const toolsFindInvocationCount = matrix.localCache === "true" ? 1 : 0;
expect(toolsFind).toBeCalledTimes(toolsFindInvocationCount);
expect(saveCache).toBeCalledTimes(matrix.cloudCache === "true" ? 1 : 0);
expect(restoreCache).toBeCalledTimes(matrix.cloudCache === "true" ? 1 : 0);

Expand All @@ -70,6 +75,10 @@ test('testing get-cmake action success with cloud/local cache enabled', async ()
});

test('testing get-cmake action success with local or cloud cache hits', async () => {
const testId = Math.random();
process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${testId}`);
process.env.RUNNER_TOOL_CACHE = path.join(os.tmpdir(), `${testId}-cache`);

for (var matrix of [
{ version: "latest", cloudCache: true, localCache: true, localHit: false, cloudHit: true },
{ version: "latest", cloudCache: false, localCache: true, localHit: false, cloudHit: false },
Expand All @@ -87,8 +96,6 @@ test('testing get-cmake action success with local or cloud cache hits', async ()
});

console.log(`\n\ntesting for: ${JSON.stringify(matrix)}:\n`)
process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${process.pid}`);
process.env.RUNNER_TOOL_CACHE = path.join(os.tmpdir(), `${process.pid}-cache`);
process.env["CUSTOM_CMAKE_VERSION"] = matrix.version;
process.env[localCacheInput] = String(matrix.localCache);
process.env[cloudCacheInput] = String(matrix.cloudCache);
Expand All @@ -97,9 +104,44 @@ test('testing get-cmake action success with local or cloud cache hits', async ()
await main();
expect(coreSetFailed).toBeCalledTimes(0);
expect(coreError).toBeCalledTimes(0);
expect(toolsFind).toBeCalledTimes(matrix.localCache ? 1 : 0);
expect(toolsCacheDir).toBeCalledTimes(!matrix.localCache || matrix.localHit ? 0 : 1);
const toolsFindInvocationCount = matrix.localCache ? 1 : 0;
expect(toolsFind).toBeCalledTimes(toolsFindInvocationCount);
const toolsCacheDirInvocationCount: number = !matrix.localCache || matrix.localHit ? 0 : 1;
expect(toolsCacheDir).toBeCalledTimes(toolsCacheDirInvocationCount);
expect(toolsFind).toHaveNthReturnedWith(1, matrix.localHit ? "hit" : "");
expect(saveCache).toBeCalledTimes((matrix.cloudHit || !matrix.cloudCache || matrix.localHit) ? 0 : 1);
expect(restoreCache).toBeCalledTimes((matrix.localHit || !matrix.cloudCache) ? 0 : 1);
}
});

test('testing get-cmake action store and restore local cache', async () => {
toolsCacheDir.mockRestore();
toolsFind.mockRestore();

const testId = Math.random();
process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${testId}`);
process.env.RUNNER_TOOL_CACHE = path.join(os.tmpdir(), `${testId}-cache`);
let downloadMock = undefined;

for (var matrix of [
{ version: "latest", cloudCache: false, localCache: true, localHit: false, cloudHit: false },
{ version: "latest", cloudCache: false, localCache: true, localHit: true, cloudHit: false },
]) {
console.log(`\n\ntesting for: ${JSON.stringify(matrix)}:\n`)
process.env["CUSTOM_CMAKE_VERSION"] = matrix.version;
process.env[localCacheInput] = String(matrix.localCache);
process.env[cloudCacheInput] = String(matrix.cloudCache);
await main();
expect(coreSetFailed).toBeCalledTimes(0);
expect(coreError).toBeCalledTimes(0);
expect(saveCache).toBeCalledTimes(0);
expect(restoreCache).toBeCalledTimes(0);
// After cache has been stored once (in the first iteration), it must be fetched from the cache and not downloaded anymore.
if (downloadMock) {
expect(downloadMock).toBeCalledTimes(0);
}

// Second iteration, check that the download function is not called because the local cache hits.
downloadMock = jest.spyOn(ToolsGetter.prototype as any, 'downloadTools');
}
});
7 changes: 6 additions & 1 deletion __tests__/cachehit.test.ts
Expand Up @@ -7,6 +7,7 @@ import * as process from 'process';
import * as os from 'os';
import { ToolsGetter } from '../src/get-cmake';
import * as cache from '@actions/cache';
import path = require('path');

// 10 minutes
jest.setTimeout(10 * 60 * 1000)
Expand All @@ -19,8 +20,12 @@ const cacheRestoreCache = jest.spyOn(cache, 'restoreCache').mockImplementation((
Promise.resolve("key")
);

const addToolsToPath = jest.spyOn(ToolsGetter.prototype as any, 'addToolsToPath').mockResolvedValue(0);

test('testing get-cmake with cache-hit...', async () => {
process.env.RUNNER_TEMP = os.tmpdir();
const testId = Math.random();
process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${testId}`);

const getter: ToolsGetter = new ToolsGetter();
await getter.run();
expect(cacheSaveCache).toBeCalledTimes(0);
Expand Down
4 changes: 3 additions & 1 deletion __tests__/cachemiss.test.ts
Expand Up @@ -8,6 +8,7 @@ import * as os from 'os';
import { ToolsGetter } from '../src/get-cmake';
import * as cache from '@actions/cache';
import * as core from '@actions/core';
import path = require('path');

// 10 minutes
jest.setTimeout(10 * 60 * 1000)
Expand All @@ -23,7 +24,8 @@ const cacheRestoreCache = jest.spyOn(cache, 'restoreCache').mockImplementation((
);

test('testing get-cmake with cache-miss...', async () => {
process.env.RUNNER_TEMP = os.tmpdir();
const testId = Math.random();
process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${testId}`);
const getter: ToolsGetter = new ToolsGetter();
await getter.run();
expect(cacheSaveCache).toBeCalledTimes(1);
Expand Down
5 changes: 4 additions & 1 deletion __tests__/cacherestorefailure.test.ts
Expand Up @@ -7,6 +7,7 @@ import * as process from 'process';
import * as os from 'os';
import { ToolsGetter } from '../src/get-cmake';
import * as cache from '@actions/cache';
import path = require('path');

// 10 minutes
jest.setTimeout(10 * 60 * 1000)
Expand All @@ -20,7 +21,9 @@ jest.spyOn(cache, 'restoreCache').mockImplementation(() => {
});

test('testing get-cmake with restoreCache failure', async () => {
process.env.RUNNER_TEMP = os.tmpdir();
const testId = Math.random();
process.env.RUNNER_TEMP = path.join(os.tmpdir(), `${testId}`);

const getter: ToolsGetter = new ToolsGetter();
await expect(getter.run()).rejects.toThrowError();
});
66 changes: 39 additions & 27 deletions dist/index.js
Expand Up @@ -45,7 +45,7 @@ function hashCode(text) {
hash = ((hash << 5) + hash) ^ char;
}
}
return hash.toString();
return Math.abs(hash);
}
class ToolsGetter {
constructor(cmakeOverride, ninjaOverride, useCloudCache = true, useLocalCache = false) {
Expand Down Expand Up @@ -119,27 +119,27 @@ class ToolsGetter {
}
get(cmakePackage, ninjaPackage) {
return __awaiter(this, void 0, void 0, function* () {
let key, outPath;
let hashedKey, outPath;
let cloudCacheHitKey = undefined;
let localCacheHit = false;
let localPath = undefined;
try {
core.startGroup(`Computing cache key from the downloads' URLs`);
// Get an unique output directory name from the URL.
const inputHash = `${cmakePackage.url}${ninjaPackage.url}`;
key = hashCode(inputHash);
core.info(`Cache key: ${key}`);
core.debug(`hash('${inputHash}') === '${key}'`);
outPath = this.getOutputPath(key);
hashedKey = hashCode(inputHash);
core.info(`Cache key: '${hashedKey}'.`);
core.debug(`hash('${inputHash}') === '${hashedKey}'`);
outPath = this.getOutputPath(hashedKey.toString());
core.info(`Local install root: '${outPath}''.`);
}
finally {
core.endGroup();
}
if (this.useLocalCache) {
try {
core.startGroup(`Restoring from local GitHub runner cache using key '${key}' into '${outPath}'`);
localPath = tools.find(ToolsGetter.LocalCacheName, key, process.platform);
core.startGroup(`Restoring from local GitHub runner cache using key '${hashedKey}'`);
localPath = tools.find(ToolsGetter.LocalCacheName, ToolsGetter.hashToFakeSemver(hashedKey), process.platform);
// Silly tool-cache API does return an empty string in case of cache miss.
localCacheHit = localPath ? true : false;
core.info(localCacheHit ? "Local cache hit." : "Local cache miss.");
Expand All @@ -151,36 +151,32 @@ class ToolsGetter {
if (!localCacheHit) {
if (this.useCloudCache) {
try {
core.startGroup(`Restoring from GitHub cloud cache using key '${key}' into '${outPath}'`);
cloudCacheHitKey = yield this.restoreCache(outPath, key);
core.startGroup(`Restoring from GitHub cloud cache using key '${hashedKey}' into '${outPath}'`);
cloudCacheHitKey = yield this.restoreCache(outPath, hashedKey);
core.info(cloudCacheHitKey === undefined ? "Cloud cache miss." : "Cloud cache hit.");
}
finally {
core.endGroup();
}
}
if (cloudCacheHitKey === undefined) {
yield core.group("Downloading and extracting CMake", () => __awaiter(this, void 0, void 0, function* () {
const downloaded = yield tools.downloadTool(cmakePackage.url);
yield extractFunction[cmakePackage.dropSuffix](downloaded, outPath);
}));
yield core.group("Downloading and extracting Ninja", () => __awaiter(this, void 0, void 0, function* () {
const downloaded = yield tools.downloadTool(ninjaPackage.url);
yield extractFunction[ninjaPackage.dropSuffix](downloaded, outPath);
}));
yield this.downloadTools(cmakePackage, ninjaPackage, outPath);
}
yield this.addToolsToPath(outPath, cmakePackage, ninjaPackage);
localPath = outPath;
}
if (!localPath) {
throw new Error(`Unexpectedly the directory of the tools is not defined`);
}
yield this.addToolsToPath(localPath, cmakePackage, ninjaPackage);
if (this.useCloudCache && cloudCacheHitKey === undefined) {
try {
core.startGroup(`Saving to GitHub cloud cache using key '${key}'`);
core.startGroup(`Saving to GitHub cloud cache using key '${hashedKey}'`);
if (localCacheHit) {
core.info("Skipping saving to cloud cache since there was local cache hit for the computed key.");
}
else if (cloudCacheHitKey === undefined) {
yield this.saveCache([outPath], key);
core.info(`Saved '${outPath}' to the GitHub cache service with key '${key}'.`);
yield this.saveCache([outPath], hashedKey);
core.info(`Saved '${outPath}' to the GitHub cache service with key '${hashedKey}'.`);
}
else {
core.info("Skipping saving to cloud cache since there was a cache hit for the computed key.");
Expand All @@ -192,9 +188,9 @@ class ToolsGetter {
}
if (this.useLocalCache && !localCacheHit && localPath) {
try {
core.startGroup(`Saving to local cache using key '${key}' from '${outPath}'`);
yield tools.cacheDir(localPath, ToolsGetter.LocalCacheName, key, process.platform);
core.info(`Saved '${outPath}' to the local GitHub runner cache with key '${key}'.`);
core.startGroup(`Saving to local cache using key '${hashedKey}' from '${outPath}'`);
yield tools.cacheDir(localPath, ToolsGetter.LocalCacheName, ToolsGetter.hashToFakeSemver(hashedKey), process.platform);
core.info(`Saved '${outPath}' to the local GitHub runner cache with key '${hashedKey}'.`);
}
finally {
core.endGroup();
Expand Down Expand Up @@ -243,7 +239,7 @@ class ToolsGetter {
saveCache(paths, key) {
return __awaiter(this, void 0, void 0, function* () {
try {
return yield cache.saveCache(paths, key);
return yield cache.saveCache(paths, key.toString());
}
catch (error) {
if (error.name === cache.ValidationError.name) {
Expand All @@ -259,7 +255,23 @@ class ToolsGetter {
});
}
restoreCache(outPath, key) {
return cache.restoreCache([outPath], key);
return cache.restoreCache([outPath], key.toString());
}
downloadTools(cmakePackage, ninjaPackage, outputPath) {
return __awaiter(this, void 0, void 0, function* () {
let outPath;
yield core.group("Downloading and extracting CMake", () => __awaiter(this, void 0, void 0, function* () {
const downloaded = yield tools.downloadTool(cmakePackage.url);
yield extractFunction[cmakePackage.dropSuffix](downloaded, outputPath);
}));
yield core.group("Downloading and extracting Ninja", () => __awaiter(this, void 0, void 0, function* () {
const downloaded = yield tools.downloadTool(ninjaPackage.url);
yield extractFunction[ninjaPackage.dropSuffix](downloaded, outputPath);
}));
});
}
static hashToFakeSemver(hashedKey) {
return `${hashedKey}.0.0`;
}
}
exports.ToolsGetter = ToolsGetter;
Expand Down

0 comments on commit de0f046

Please sign in to comment.