diff --git a/cypress/e2e/execute.cy.ts b/cypress/e2e/execute.cy.ts new file mode 100644 index 000000000..9616f3a5e --- /dev/null +++ b/cypress/e2e/execute.cy.ts @@ -0,0 +1,83 @@ +// Copyright (c) 2026, 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 { + assertNoConsoleOutput, + compilerOutput, + findPane, + monacoEditorTextShouldContain, + openExecutor, + setMonacoEditorContent, + setupAndWaitForCompilation, + visitPage, + waitForEditors, +} from '../support/utils'; + +function executorPane() { + return findPane('Executor'); +} + +beforeEach(visitPage); + +afterEach(() => { + return cy.window().then(_win => { + assertNoConsoleOutput(); + }); +}); + +describe('Executor', () => { + it('should open an executor pane from the compiler toolbar', () => { + setupAndWaitForCompilation(); + openExecutor(); + executorPane().should('exist'); + }); + + it('should show program stdout', () => { + waitForEditors(); + setMonacoEditorContent(`\ +#include +int main() { printf("hello from cypress"); return 0; }`); + monacoEditorTextShouldContain(compilerOutput(), 'main'); + openExecutor(); + executorPane().find('.execution-stdout', {timeout: 15000}).should('contain.text', 'hello from cypress'); + }); + + it('should show non-zero exit code', () => { + waitForEditors(); + setMonacoEditorContent('int main() { return 42; }'); + monacoEditorTextShouldContain(compilerOutput(), 'main'); + openExecutor(); + executorPane().find('.execution-output', {timeout: 15000}).should('contain.text', 'Program returned: 42'); + }); + + it('should show stderr output', () => { + waitForEditors(); + setMonacoEditorContent(`\ +#include +int main() { fprintf(stderr, "error output"); return 0; }`); + monacoEditorTextShouldContain(compilerOutput(), 'main'); + openExecutor(); + executorPane().find('.execution-output', {timeout: 15000}).should('contain.text', 'error output'); + }); +}); diff --git a/cypress/e2e/gccdump.cy.ts b/cypress/e2e/gccdump.cy.ts new file mode 100644 index 000000000..e053d5149 --- /dev/null +++ b/cypress/e2e/gccdump.cy.ts @@ -0,0 +1,67 @@ +// Copyright (c) 2026, 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 { + assertNoConsoleOutput, + findPane, + monacoEditorTextShouldContain, + openGccDump, + setupAndWaitForCompilation, + visitPage, +} from '../support/utils'; + +function gccDumpPane() { + return findPane('GCC Tree'); +} + +beforeEach(visitPage); + +afterEach(() => { + return cy.window().then(_win => { + assertNoConsoleOutput(); + }); +}); + +describe('GCC Tree/RTL dump', () => { + it('should open a GCC dump pane from the compiler toolbar', () => { + setupAndWaitForCompilation(); + openGccDump(); + gccDumpPane().should('exist'); + }); + + it('should show a pass picker with available passes', () => { + setupAndWaitForCompilation(); + openGccDump(); + cy.get('.gccdump-pass-picker + .ts-wrapper .ts-control', {timeout: 10000}).should('be.visible').click(); + cy.get('.ts-dropdown .option:visible', {timeout: 10000}).should('have.length.greaterThan', 0); + }); + + it('should display tree dump content when a pass is selected', () => { + setupAndWaitForCompilation(); + openGccDump(); + cy.get('.gccdump-pass-picker + .ts-wrapper .ts-control', {timeout: 10000}).should('be.visible').click(); + cy.get('.ts-dropdown .option:visible', {timeout: 10000}).first().click(); + monacoEditorTextShouldContain(gccDumpPane().find('.monaco-editor'), 'square'); + }); +}); diff --git a/cypress/e2e/opt-view.cy.ts b/cypress/e2e/opt-view.cy.ts new file mode 100644 index 000000000..444e9bd29 --- /dev/null +++ b/cypress/e2e/opt-view.cy.ts @@ -0,0 +1,49 @@ +// Copyright (c) 2026, 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 {assertNoConsoleOutput, findPane, openOptRemarks, visitPage, waitForEditors} from '../support/utils'; + +function optPane() { + return findPane('Opt'); +} + +beforeEach(visitPage); + +afterEach(() => { + return cy.window().then(_win => { + assertNoConsoleOutput(); + }); +}); + +describe('Optimisation remarks', () => { + it('should open an opt remarks pane from the compiler toolbar', () => { + waitForEditors(); + openOptRemarks(); + optPane().should('exist'); + }); + + // TODO: Testing actual opt remark content requires a scriptable/canned compiler + // so we can guarantee remarks are produced regardless of GCC version. + // See discussion about a minimal test harness compiler. +}); diff --git a/cypress/e2e/pp-view.cy.ts b/cypress/e2e/pp-view.cy.ts new file mode 100644 index 000000000..cbb86f14d --- /dev/null +++ b/cypress/e2e/pp-view.cy.ts @@ -0,0 +1,88 @@ +// Copyright (c) 2026, 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 { + assertNoConsoleOutput, + findPane, + monacoEditorTextShouldContain, + monacoEditorTextShouldNotContain, + openPreprocessor, + setMonacoEditorContent, + setupAndWaitForCompilation, + visitPage, + waitForEditors, +} from '../support/utils'; + +function ppPane() { + return findPane('Preprocessor'); +} + +beforeEach(visitPage); + +afterEach(() => { + return cy.window().then(_win => { + assertNoConsoleOutput(); + }); +}); + +describe('Preprocessor output', () => { + it('should open a preprocessor pane from the compiler toolbar', () => { + setupAndWaitForCompilation(); + openPreprocessor(); + ppPane().should('exist'); + }); + + it('should show expanded #define substitution', () => { + setMonacoEditorContent(`\ +#define MAGIC 42 +int get_magic() { return MAGIC; }`); + waitForEditors(); + openPreprocessor(); + monacoEditorTextShouldContain(ppPane().find('.monaco-editor'), '42'); + }); + + it('should expand macros from #include', () => { + setMonacoEditorContent(`\ +#include +int max_int() { return INT_MAX; }`); + waitForEditors(); + openPreprocessor(); + monacoEditorTextShouldContain(ppPane().find('.monaco-editor'), 'max_int'); + monacoEditorTextShouldNotContain(ppPane().find('.monaco-editor'), 'INT_MAX'); + }); + + it('should update when source changes', () => { + setMonacoEditorContent(`\ +#define VALUE_A 100 +int a() { return VALUE_A; }`); + waitForEditors(); + openPreprocessor(); + monacoEditorTextShouldContain(ppPane().find('.monaco-editor'), '100'); + + setMonacoEditorContent(`\ +#define VALUE_B 999 +int b() { return VALUE_B; }`); + monacoEditorTextShouldContain(ppPane().find('.monaco-editor'), '999'); + }); +}); diff --git a/cypress/e2e/shortcuts.cy.ts b/cypress/e2e/shortcuts.cy.ts new file mode 100644 index 000000000..a287838e1 --- /dev/null +++ b/cypress/e2e/shortcuts.cy.ts @@ -0,0 +1,74 @@ +// Copyright (c) 2026, 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 { + assertNoConsoleOutput, + compilerOutput, + compilerPane, + monacoEditorTextShouldContain, + monacoEditorTextShouldNotContain, + setMonacoEditorContent, + sourceEditor, + stubConsoleOutput, + waitForEditors, +} from '../support/utils'; + +beforeEach(() => { + cy.visit('/', { + onBeforeLoad: win => { + stubConsoleOutput(win); + // Disable auto-compile so we can test that Ctrl+Enter explicitly triggers it + win.localStorage.setItem('settings', JSON.stringify({compileOnChange: false})); + }, + }); +}); + +afterEach(() => { + return cy.window().then(_win => { + assertNoConsoleOutput(); + }); +}); + +describe('Keyboard shortcuts', () => { + it('should recompile with Ctrl+Enter when compileOnChange is disabled', () => { + waitForEditors(); + setMonacoEditorContent(`\ +#ifdef SHORTCUT_TEST +int shortcut_active(void) { return 1; } +#else +int shortcut_inactive(void) { return 0; } +#endif`); + + // With compileOnChange off, changing options should NOT recompile + compilerPane().find('input.options').clear().type('-DSHORTCUT_TEST'); + + // Output should still show the old compilation result (no -D flag) + monacoEditorTextShouldNotContain(compilerOutput(), 'shortcut_active'); + + // Ctrl+Enter should trigger recompilation + sourceEditor().find('textarea').type('{ctrl}{enter}', {force: true}); + + monacoEditorTextShouldContain(compilerOutput(), 'shortcut_active'); + }); +}); diff --git a/cypress/support/utils.ts b/cypress/support/utils.ts index 7f637828a..4d97d1fe9 100644 --- a/cypress/support/utils.ts +++ b/cypress/support/utils.ts @@ -171,6 +171,36 @@ export function addCompilerFromCompilerPane() { cy.get('[data-cy="new-add-compiler-btn"]:visible').first().click(); } +/** + * Open a pane from the compiler toolbar's "Add new" dropdown by its data-cy button id. + * The buttonId should match the data-cy attribute without the "new-" prefix and "-btn" suffix, + * e.g. "create-executor" for `data-cy="new-create-executor-btn"`. + */ +export function openPaneFromCompiler(buttonId: string) { + compilerPane().find('[data-cy="new-compiler-dropdown-btn"]:visible').first().click(); + cy.get(`[data-cy="new-${buttonId}-btn"]:visible`).first().click(); +} + +/** Open the executor pane from the compiler's "Add new" dropdown. */ +export function openExecutor() { + openPaneFromCompiler('create-executor'); +} + +/** Open the GCC Tree/RTL dump pane from the compiler's "Add new" dropdown. */ +export function openGccDump() { + openPaneFromCompiler('view-gccdump'); +} + +/** Open the preprocessor pane from the compiler's "Add new" dropdown. */ +export function openPreprocessor() { + openPaneFromCompiler('view-pp'); +} + +/** Open the optimisation remarks pane from the compiler's "Add new" dropdown. */ +export function openOptRemarks() { + openPaneFromCompiler('view-optimization'); +} + /** Open the conformance view from the source editor's "Add new" dropdown. */ export function openConformanceView() { findPane('source').find('[data-cy="new-editor-dropdown-btn"]').click();