@@ -18,6 +18,7 @@ import {
1818 ParamsSource ,
1919 resolveBuildFlags ,
2020 runBuild ,
21+ SamBuildResult ,
2122} from '../../../shared/sam/build'
2223import { TreeNode } from '../../../shared/treeview/resourceTreeDataProvider'
2324import { createWizardTester } from '../wizards/wizardTestUtils'
@@ -34,6 +35,7 @@ import { samconfigCompleteData, validTemplateData } from './samTestUtils'
3435import { CloudFormationTemplateRegistry } from '../../../shared/fs/templateRegistry'
3536import { getTestWindow } from '../vscode/window'
3637import { CancellationError } from '../../../shared/utilities/timeoutUtils'
38+ import { SamAppLocation } from '../../../awsService/appBuilder/explorer/samProject'
3739import { SemVer } from 'semver'
3840
3941describe ( 'SAM BuildWizard' , async function ( ) {
@@ -423,22 +425,8 @@ describe('SAM runBuild', () => {
423425 verifyCorrectDependencyCall ( )
424426 } )
425427
426- it ( '[entry: appbuilder node] with default flags should instantiate correct process in terminal' , async ( ) => {
427- const prompterTester = PrompterTester . init ( )
428- . handleQuickPick ( 'Specify parameter source for build' , async ( quickPick ) => {
429- // Need sometime to wait for the template to search for template file
430- await quickPick . untilReady ( )
431-
432- assert . strictEqual ( quickPick . items . length , 2 )
433- const items = quickPick . items
434- assert . strictEqual ( quickPick . items . length , 2 )
435- assert . deepStrictEqual ( items [ 0 ] , { data : ParamsSource . Specify , label : 'Specify build flags' } )
436- assert . deepStrictEqual ( items [ 1 ] . label , 'Use default values' )
437- quickPick . acceptItem ( quickPick . items [ 1 ] )
438- } )
439- . build ( )
440-
441- // Invoke sync command from command palette
428+ it ( '[entry: appbuilder node] with default flags should instantiate correct process in terminal and show progress notification' , async ( ) => {
429+ const prompterTester = getPrompterTester ( )
442430 const expectedSamAppLocation = {
443431 workspaceFolder : workspaceFolder ,
444432 samTemplateUri : templateFile ,
@@ -447,6 +435,10 @@ describe('SAM runBuild', () => {
447435
448436 await runBuild ( new AppNode ( expectedSamAppLocation ) )
449437
438+ getTestWindow ( )
439+ . getFirstMessage ( )
440+ . assertProgress ( `Building SAM template at ${ expectedSamAppLocation . samTemplateUri . path } ` )
441+
450442 assert . deepEqual ( mockChildProcessClass . getCall ( 0 ) . args , [
451443 'sam-cli-path' ,
452444 [
@@ -472,6 +464,27 @@ describe('SAM runBuild', () => {
472464 prompterTester . assertCallAll ( )
473465 } )
474466
467+ it ( '[entry: appbuilder node] should throw an error when running two build processes in parallel for the same template' , async ( ) => {
468+ const prompterTester = getPrompterTester ( )
469+ const expectedSamAppLocation = {
470+ workspaceFolder : workspaceFolder ,
471+ samTemplateUri : templateFile ,
472+ projectRoot : projectRoot ,
473+ }
474+ await assert . rejects (
475+ async ( ) => {
476+ await runInParallel ( expectedSamAppLocation )
477+ } ,
478+ ( e : any ) => {
479+ assert . strictEqual ( e instanceof ToolkitError , true )
480+ assert . strictEqual ( e . message , 'This template is already being built' )
481+ assert . strictEqual ( e . code , 'BuildInProgress' )
482+ return true
483+ }
484+ )
485+ prompterTester . assertCallAll ( undefined , 2 )
486+ } )
487+
475488 it ( '[entry: command palette] use samconfig should instantiate correct process in terminal' , async ( ) => {
476489 const samconfigFile = vscode . Uri . file ( await testFolder . write ( 'samconfig.toml' , samconfigCompleteData ) )
477490
@@ -587,6 +600,38 @@ describe('SAM runBuild', () => {
587600 } )
588601} )
589602
603+ async function runInParallel ( samLocation : SamAppLocation ) : Promise < [ SamBuildResult , SamBuildResult ] > {
604+ return Promise . all ( [ runBuild ( new AppNode ( samLocation ) ) , delayedRunBuild ( samLocation ) ] )
605+ }
606+
607+ // We add a small delay to avoid the unlikely but possible race condition.
608+ async function delayedRunBuild ( samLocation : SamAppLocation ) : Promise < SamBuildResult > {
609+ return new Promise ( async ( resolve , reject ) => {
610+ // Add a small delay before returning the build promise
611+ setTimeout ( ( ) => {
612+ // Do nothing, just let the delay pass
613+ } , 20 )
614+
615+ const buildPromise = runBuild ( new AppNode ( samLocation ) )
616+ buildPromise . then ( resolve ) . catch ( reject )
617+ } )
618+ }
619+
620+ function getPrompterTester ( ) {
621+ return PrompterTester . init ( )
622+ . handleQuickPick ( 'Specify parameter source for build' , async ( quickPick ) => {
623+ // Need sometime to wait for the template to search for template file
624+ await quickPick . untilReady ( )
625+
626+ assert . strictEqual ( quickPick . items . length , 2 )
627+ const items = quickPick . items
628+ assert . strictEqual ( quickPick . items . length , 2 )
629+ assert . deepStrictEqual ( items [ 0 ] , { data : ParamsSource . Specify , label : 'Specify build flags' } )
630+ assert . deepStrictEqual ( items [ 1 ] . label , 'Use default values' )
631+ quickPick . acceptItem ( quickPick . items [ 1 ] )
632+ } )
633+ . build ( )
634+ }
590635function testResolveBuildFlags (
591636 sandbox : sinon . SinonSandbox ,
592637 parsedVersion : SemVer ,
0 commit comments