diff --git a/cypress/e2e/remote-id.cy.ts b/cypress/e2e/remote-id.cy.ts new file mode 100644 index 000000000..c043eca90 --- /dev/null +++ b/cypress/e2e/remote-id.cy.ts @@ -0,0 +1,9 @@ +import {runFrontendTest} from '../support/utils'; + +describe('RemoteId testing', () => { + before(() => { + cy.visit('/'); + }); + + runFrontendTest('remoteId'); +}); diff --git a/lib/options-handler.ts b/lib/options-handler.ts index 6b477d350..fb8bf15fe 100755 --- a/lib/options-handler.ts +++ b/lib/options-handler.ts @@ -37,6 +37,7 @@ import type {Source} from '../types/source.interfaces.js'; import type {ToolTypeKey} from '../types/tool.interfaces.js'; import {AppArguments} from './app.interfaces.js'; +import {getRemoteId} from '../shared/remote-utils.js'; import {logger} from './logger.js'; import type {PropertyGetter, PropertyValue} from './properties.interfaces.js'; import {CompilerProps} from './properties.js'; @@ -376,11 +377,6 @@ export class ClientOptionsHandler { return libraries; } - getRemoteId(remoteUrl: string, language: LanguageKey) { - const url = new URL(remoteUrl); - return url.host.replaceAll('.', '_') + '_' + language; - } - libArrayToObject(libsArr: any[]) { const libs: Record = {}; for (const lib of libsArr) { @@ -397,9 +393,9 @@ export class ClientOptionsHandler { } async getRemoteLibraries(language: LanguageKey, remoteUrl: string) { - const remoteId = this.getRemoteId(remoteUrl, language); + const remoteId = getRemoteId(remoteUrl, language); if (!this.remoteLibs[remoteId]) { - return new Promise(resolve => { + return await new Promise(resolve => { const url = ClientOptionsHandler.getRemoteUrlForLibraries(remoteUrl, language); logger.info(`Fetching remote libraries from ${url}`); let fullData = ''; diff --git a/shared/remote-utils.ts b/shared/remote-utils.ts new file mode 100644 index 000000000..23b5c083b --- /dev/null +++ b/shared/remote-utils.ts @@ -0,0 +1,28 @@ +// Copyright (c) 2025, Compiler Explorer Authors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +export function getRemoteId(remoteUrl: string, language: string): string { + const url: URL = new URL(remoteUrl); + return url.host.replace(/\./g, '_') + url.pathname.replace(/\//g, '_') + '_' + language; +} diff --git a/shared/url-testcases.ts b/shared/url-testcases.ts new file mode 100644 index 000000000..dc3616891 --- /dev/null +++ b/shared/url-testcases.ts @@ -0,0 +1,32 @@ +export const UrlTestCases = [ + { + remoteUrl: 'https://example.com/path/to/resource', + language: 'cpp', + expectedId: 'example_com_path_to_resource_cpp', + }, + { + remoteUrl: 'https://example.com', + language: 'java', + expectedId: 'example_com__java', + }, + { + remoteUrl: 'https://sub.domain.com/a/b/c/', + language: 'rust', + expectedId: 'sub_domain_com_a_b_c__rust', + }, + { + remoteUrl: 'https://godbolt.org:443/gpu', + language: 'c++', + expectedId: 'godbolt_org_gpu_c++', + }, + { + remoteUrl: 'https://godbolt.org:443/winprod', + language: 'c++', + expectedId: 'godbolt_org_winprod_c++', + }, + { + remoteUrl: 'https://godbolt.org:443', + language: 'c++', + expectedId: 'godbolt_org__c++', + }, +]; diff --git a/static/lib-utils.ts b/static/lib-utils.ts index 88f111c35..f1a5b2b68 100644 --- a/static/lib-utils.ts +++ b/static/lib-utils.ts @@ -22,19 +22,15 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {getRemoteId} from '../shared/remote-utils.js'; import {Remote} from './compiler.interfaces.js'; import {LanguageLibs, Library} from './options.interfaces.js'; import {options} from './options.js'; const LIB_MATCH_RE = /([\w-]*)\.([\w-]*)/i; -function getRemoteId(language: string, remoteUrl: string): string { - const url: URL = new URL(remoteUrl); - return url.host.replace(/\./g, '_') + '_' + language; -} - function getRemoteLibraries(language: string, remoteUrl: string): LanguageLibs { - const remoteId = getRemoteId(language, remoteUrl); + const remoteId = getRemoteId(remoteUrl, language); return options.remoteLibs[remoteId]; } @@ -78,7 +74,7 @@ export function getSupportedLibraries( } return allLibs; } - const allLibs = getRemoteLibraries(langId, remote.target); + const allLibs = getRemoteLibraries(langId, remote.target + remote.basePath); if (supportedLibrariesArr && supportedLibrariesArr.length > 0) { return copyAndFilterLibraries(allLibs, supportedLibrariesArr); } diff --git a/static/tests/_all.ts b/static/tests/_all.ts index 0f2f1ec18..6c9b96803 100644 --- a/static/tests/_all.ts +++ b/static/tests/_all.ts @@ -25,3 +25,4 @@ import './frontend-testing'; import './hello-world'; import './motd'; +import './remote-id'; diff --git a/static/tests/remote-id.ts b/static/tests/remote-id.ts new file mode 100644 index 000000000..3d86d6b78 --- /dev/null +++ b/static/tests/remote-id.ts @@ -0,0 +1,47 @@ +// Copyright (c) 2025, Compiler Explorer Authors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +/// + +import {getRemoteId} from '../../shared/remote-utils.js'; + +import {UrlTestCases} from '../../shared/url-testcases.js'; + +import {ITestable} from './frontend-testing.interfaces.js'; + +class RemoteIdTests implements ITestable { + public readonly description: string = 'remoteId'; + + public async run() { + UrlTestCases.forEach(testCase => { + if (getRemoteId(testCase.remoteUrl, testCase.language) !== testCase.expectedId) { + throw new Error( + `Test case failed for language: ${testCase.language}, remoteUrl: ${testCase.remoteUrl}, expectedId: ${testCase.expectedId}`, + ); + } + }); + } +} + +window.compilerExplorerFrontendTesting.add(new RemoteIdTests()); diff --git a/test/options-handler.ts b/test/options-handler.ts index a8d4653dc..8e36e18b7 100644 --- a/test/options-handler.ts +++ b/test/options-handler.ts @@ -37,6 +37,9 @@ import {BaseTool} from '../lib/tooling/base-tool.js'; import {CompilerInfo} from '../types/compiler.interfaces.js'; import {LanguageKey} from '../types/languages.interfaces.js'; +import {getRemoteId} from '../shared/remote-utils.js'; +import {UrlTestCases} from '../shared/url-testcases.js'; + import {makeFakeCompilerInfo} from './utils.js'; const languages = { @@ -593,4 +596,13 @@ describe('Options handler', () => { expect(librariesUrl).toEqual('https://godbolt.org:443/gpu/api/libraries/c++'); }); + + describe('getRemoteId', () => { + UrlTestCases.forEach(testCase => { + it(`should generate remote ID for URL "${testCase.remoteUrl}" with language "${testCase.language}"`, () => { + const result = getRemoteId(testCase.remoteUrl, testCase.language); + expect(result).toBe(testCase.expectedId); + }); + }); + }); });