From add6c921955c54651317791429ddc59a6bf66e3a Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Tue, 29 Nov 2022 15:21:45 +0100 Subject: [PATCH] [INTERNAL] Clone resource when reading from Memory --- lib/Resource.js | 2 + lib/adapters/Memory.js | 66 +++++++++++++++++--------------- test/lib/Resource.js | 24 ++++++++++++ test/lib/adapters/Memory_read.js | 56 +++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 31 deletions(-) diff --git a/lib/Resource.js b/lib/Resource.js index 29737739..39b31ab4 100644 --- a/lib/Resource.js +++ b/lib/Resource.js @@ -323,6 +323,8 @@ class Resource { options.createStream = this._createStream; } else if (this._buffer) { options.buffer = this._buffer; + } else if (this.__project) { + options.project = this.__project; } return options; diff --git a/lib/adapters/Memory.js b/lib/adapters/Memory.js index 4d4c0ac1..afe507b2 100644 --- a/lib/adapters/Memory.js +++ b/lib/adapters/Memory.js @@ -27,6 +27,24 @@ class Memory extends AbstractAdapter { this._virDirs = Object.create(null); // map full of directories } + /** + * Matches and returns resources from a given map (either _virFiles or _virDirs). + * + * @private + * @param {string[]} patterns + * @param {object} resourceMap + * @returns {Promise} + */ + async _matchPatterns(patterns, resourceMap) { + const resourcePaths = Object.keys(resourceMap); + const matchedPaths = micromatch(resourcePaths, patterns, { + dot: true + }); + return Promise.all(matchedPaths.map((virPath) => { + return resourceMap[virPath] && resourceMap[virPath].clone(); + })); + } + /** * Locate resources by glob. * @@ -55,22 +73,11 @@ class Memory extends AbstractAdapter { ]; } - const filePaths = Object.keys(this._virFiles); - const matchedFilePaths = micromatch(filePaths, patterns, { - dot: true - }); - let matchedResources = matchedFilePaths.map((virPath) => { - return this._virFiles[virPath]; - }); + let matchedResources = await this._matchPatterns(patterns, this._virFiles); if (!options.nodir) { - const dirPaths = Object.keys(this._virDirs); - const matchedDirs = micromatch(dirPaths, patterns, { - dot: true - }); - matchedResources = matchedResources.concat(matchedDirs.map((virPath) => { - return this._virDirs[virPath]; - })); + const matchedDirs = await this._matchPatterns(patterns, this._virDirs); + matchedResources = matchedResources.concat(matchedDirs); } return matchedResources; @@ -85,28 +92,25 @@ class Memory extends AbstractAdapter { * @param {@ui5/fs/tracing.Trace} trace Trace instance * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource */ - _byPath(virPath, options, trace) { + async _byPath(virPath, options, trace) { if (this.isPathExcluded(virPath)) { - return Promise.resolve(null); + return null; + } + if (!virPath.startsWith(this._virBasePath) && virPath !== this._virBaseDir) { + // Neither starts with basePath, nor equals baseDirectory + return null; } - return new Promise((resolve, reject) => { - if (!virPath.startsWith(this._virBasePath) && virPath !== this._virBaseDir) { - // Neither starts with basePath, nor equals baseDirectory - resolve(null); - return; - } - const relPath = virPath.substr(this._virBasePath.length); - trace.pathCall(); + const relPath = virPath.substr(this._virBasePath.length); + trace.pathCall(); - const resource = this._virFiles[relPath]; + const resource = this._virFiles[relPath]; - if (!resource || (options.nodir && resource.getStatInfo().isDirectory())) { - resolve(null); - } else { - resolve(resource); - } - }); + if (!resource || (options.nodir && resource.getStatInfo().isDirectory())) { + return null; + } else { + return await resource.clone(); + } } /** diff --git a/test/lib/Resource.js b/test/lib/Resource.js index a3f6cb44..6a86303a 100644 --- a/test/lib/Resource.js +++ b/test/lib/Resource.js @@ -316,6 +316,30 @@ test("Resource: clone resource with stream", async (t) => { t.is(clonedResourceContent, "Content", "Cloned resource has correct content string"); }); +test("Resource: clone resource with project", async (t) => { + t.plan(2); + + const myProject = { + name: "my project" + }; + const resourceOptions = { + path: "my/path/to/resource", + project: myProject + }; + + const resource = new Resource({ + path: "my/path/to/resource", + project: myProject + }); + + const clonedResource = await resource.clone(); + t.pass("Resource cloned"); + + const clonedResourceProject = await clonedResource.getProject(); + t.is(clonedResourceProject, resourceOptions.project, "Cloned resource should have same " + + "project reference as the original resource"); +}); + test("getStream with createStream callback content: Subsequent content requests should throw error due " + "to drained content", async (t) => { const resource = createBasicResource(); diff --git a/test/lib/adapters/Memory_read.js b/test/lib/adapters/Memory_read.js index 09b99159..444116df 100644 --- a/test/lib/adapters/Memory_read.js +++ b/test/lib/adapters/Memory_read.js @@ -605,3 +605,59 @@ test("static excludes: glob with negated directory exclude, not excluding resour t.is(resources.length, 4, "Found two resources and two directories"); }); + +test("byPath returns new resource", async (t) => { + const originalResource = createResource({ + path: "/app/index.html", + string: "test" + }); + + const memoryAdapter = createAdapter({virBasePath: "/"}); + + await memoryAdapter.write(originalResource); + + const returnedResource = await memoryAdapter.byPath("/app/index.html"); + + t.deepEqual(returnedResource, originalResource, + "Returned resource should be deep equal to original resource"); + t.not(returnedResource, originalResource, + "Returned resource should not have same reference as original resource"); + + const anotherReturnedResource = await memoryAdapter.byPath("/app/index.html"); + + t.deepEqual(anotherReturnedResource, originalResource, + "Returned resource should be deep equal to original resource"); + t.not(anotherReturnedResource, originalResource, + "Returned resource should not have same reference as original resource"); + + t.not(returnedResource, anotherReturnedResource, + "Both returned resources should not have same reference"); +}); + +test("byGlob returns new resources", async (t) => { + const originalResource = createResource({ + path: "/app/index.html", + string: "test" + }); + + const memoryAdapter = createAdapter({virBasePath: "/"}); + + await memoryAdapter.write(originalResource); + + const [returnedResource] = await memoryAdapter.byGlob("/**"); + + t.deepEqual(returnedResource, originalResource, + "Returned resource should be deep equal to the original resource"); + t.not(returnedResource, originalResource, + "Returned resource should not have same reference as the original resource"); + + const [anotherReturnedResource] = await memoryAdapter.byGlob("/**"); + + t.deepEqual(anotherReturnedResource, originalResource, + "Another returned resource should be deep equal to the original resource"); + t.not(anotherReturnedResource, originalResource, + "Another returned resource should not have same reference as the original resource"); + + t.not(returnedResource, anotherReturnedResource, + "Both returned resources should not have same reference"); +});