@@ -662,6 +662,209 @@ func TestBuildJavaScript(t *testing.T) {
662662 }
663663}
664664
665+ func TestBuildCPP (t * testing.T ) {
666+ if os .Getenv ("TEST_COMPUTE_BUILD_CPP" ) == "" && os .Getenv ("TEST_COMPUTE_BUILD" ) == "" {
667+ t .Log ("skipping test" )
668+ t .Skip ("Set TEST_COMPUTE_BUILD to run this test" )
669+ }
670+
671+ args := testutil .SplitArgs
672+
673+ scenarios := []struct {
674+ name string
675+ args []string
676+ applicationConfig * config.File
677+ fastlyManifest string
678+ wantError string
679+ wantRemediationError string
680+ wantOutput []string
681+ }{
682+ {
683+ name : "no fastly.toml manifest" ,
684+ args : args ("compute build" ),
685+ wantError : "error reading fastly.toml: file not found" ,
686+ wantRemediationError : "Run `fastly compute init` to ensure a correctly configured manifest." ,
687+ },
688+ {
689+ name : "empty language" ,
690+ args : args ("compute build" ),
691+ fastlyManifest : `
692+ manifest_version = 2
693+ name = "test"` ,
694+ wantError : "language cannot be empty, please provide a language" ,
695+ },
696+ {
697+ name : "unknown language" ,
698+ args : args ("compute build" ),
699+ fastlyManifest : `
700+ manifest_version = 2
701+ name = "test"
702+ language = "foobar"` ,
703+ wantError : "unsupported language foobar" ,
704+ },
705+ // The following test validates that the project compiles successfully even
706+ // though the fastly.toml manifest has no build script. There should be a
707+ // default build script inserted.
708+ //
709+ // NOTE: This test passes --verbose so we can validate specific outputs.
710+ {
711+ name : "build script inserted dynamically when missing" ,
712+ args : args ("compute build --verbose" ),
713+ applicationConfig : & config.File {
714+ Language : config.Language {
715+ CPP : config.CPP {
716+ ToolchainConstraint : ">= 14.0.0" ,
717+ WasmWasiTarget : "wasm32-wasip1" ,
718+ },
719+ },
720+ },
721+ fastlyManifest : `
722+ manifest_version = 2
723+ name = "test"
724+ language = "cpp"` ,
725+ wantOutput : []string {
726+ "No [scripts.build] found in fastly.toml." , // requires --verbose
727+ "The following default build command for C++ will be used" ,
728+ "clang++ -O3 --target=wasm32-wasip1 -o ./bin/main.wasm main.cpp" ,
729+ },
730+ },
731+ {
732+ name : "build error" ,
733+ args : args ("compute build" ),
734+ applicationConfig : & config.File {
735+ Language : config.Language {
736+ CPP : config.CPP {
737+ ToolchainConstraint : ">= 14.0.0" ,
738+ WasmWasiTarget : "wasm32-wasip1" ,
739+ },
740+ },
741+ },
742+ fastlyManifest : `
743+ manifest_version = 2
744+ name = "test"
745+ language = "cpp"
746+
747+ [scripts]
748+ build = "echo no compilation happening"` ,
749+ wantRemediationError : compute .DefaultBuildErrorRemediation ,
750+ },
751+ // NOTE: This test passes --verbose so we can validate specific outputs.
752+ {
753+ name : "successful build" ,
754+ args : args ("compute build --verbose" ),
755+ applicationConfig : & config.File {
756+ Language : config.Language {
757+ CPP : config.CPP {
758+ ToolchainConstraint : ">= 14.0.0" ,
759+ WasmWasiTarget : "wasm32-wasip1" ,
760+ },
761+ },
762+ },
763+ fastlyManifest : `
764+ manifest_version = 2
765+ name = "test"
766+ language = "cpp"
767+
768+ [scripts]
769+ build = "clang++ -O3 --target=wasm32-wasip1 -o bin/main.wasm main.cpp"` ,
770+ wantOutput : []string {
771+ "Creating ./bin directory (for Wasm binary)" ,
772+ "Built package" ,
773+ },
774+ },
775+ }
776+ for testcaseIdx := range scenarios {
777+ testcase := & scenarios [testcaseIdx ]
778+ t .Run (testcase .name , func (t * testing.T ) {
779+ // We're going to chdir to a build environment,
780+ // so save the PWD to return to, afterwards.
781+ pwd , err := os .Getwd ()
782+ if err != nil {
783+ t .Fatal (err )
784+ }
785+
786+ wasmtoolsBinName := "wasm-tools"
787+
788+ // Windows was having issues when trying to move a tmpBin file (which
789+ // represents the latest binary downloaded from GitHub) to binPath (which
790+ // represents the existing binary installed on a user's machine).
791+ //
792+ // The problem was, for the sake of the tests, I just create one file
793+ // `wasmtoolsBinName` and used that for both `tmpBin` and `binPath` and
794+ // this works fine on *nix systems. But once Windows did `os.Rename()` and
795+ // move tmpBin to binPath it would no longer be able to set permissions on
796+ // the binPath because it didn't think the file existed any more. My guess
797+ // is that moving a file over itself causes Windows to remove the file.
798+ //
799+ // So to work around that issue I just create two separate files because
800+ // in reality that's what the CLI will be dealing with. I only used one
801+ // file for the sake of test case convenience (which ironically became
802+ // very INCONVENIENT when the tests started unexpectedly failing on
803+ // Windows and caused me a long time debugging).
804+ latestDownloaded := wasmtoolsBinName + "-latest-downloaded"
805+
806+ // Create test environment
807+ rootdir := testutil .NewEnv (testutil.EnvOpts {
808+ T : t ,
809+ Copy : []testutil.FileIO {
810+ {Src : filepath .Join ("testdata" , "build" , "cpp" , "main.cpp" ), Dst : "main.cpp" },
811+ },
812+ Write : []testutil.FileIO {
813+ {Src : `#!/usr/bin/env bash
814+ echo wasm-tools 1.0.4` , Dst : wasmtoolsBinName , Executable : true },
815+ {Src : `#!/usr/bin/env bash
816+ echo wasm-tools 2.0.0` , Dst : latestDownloaded , Executable : true },
817+ {Src : testcase .fastlyManifest , Dst : manifest .Filename },
818+ },
819+ })
820+ defer os .RemoveAll (rootdir )
821+ wasmtoolsBinPath := filepath .Join (rootdir , wasmtoolsBinName )
822+
823+ // Before running the test, chdir into the build environment.
824+ // When we're done, chdir back to our original location.
825+ // This is so we can reliably copy the testdata/ fixtures.
826+ if err := os .Chdir (rootdir ); err != nil {
827+ t .Fatal (err )
828+ }
829+ defer func () {
830+ _ = os .Chdir (pwd )
831+ }()
832+
833+ var stdout threadsafe.Buffer
834+ app .Init = func (_ []string , _ io.Reader ) (* global.Data , error ) {
835+ opts := testutil .MockGlobalData (testcase .args , & stdout )
836+ if testcase .applicationConfig != nil {
837+ opts .Config = * testcase .applicationConfig
838+ }
839+ opts .Versioners = global.Versioners {
840+ WasmTools : mock.AssetVersioner {
841+ AssetVersion : "1.2.3" ,
842+ BinaryFilename : wasmtoolsBinName ,
843+ DownloadOK : true ,
844+ DownloadedFile : latestDownloaded ,
845+ InstallFilePath : wasmtoolsBinPath , // avoid overwriting developer's actual wasm-tools install
846+ },
847+ }
848+ return opts , nil
849+ }
850+ err = app .Run (testcase .args , nil )
851+
852+ t .Log (stdout .String ())
853+
854+ testutil .AssertRemediationErrorContains (t , err , testcase .wantRemediationError )
855+
856+ // NOTE: Some errors we want to assert only the remediation.
857+ // e.g. a 'stat' error isn't the same across operating systems/platforms.
858+ if testcase .wantError != "" {
859+ testutil .AssertErrorContains (t , err , testcase .wantError )
860+ }
861+ for _ , s := range testcase .wantOutput {
862+ testutil .AssertStringContains (t , stdout .String (), s )
863+ }
864+ })
865+ }
866+ }
867+
665868// NOTE: TestBuildOther also validates the post_build settings.
666869func TestBuildOther (t * testing.T ) {
667870 args := testutil .SplitArgs
0 commit comments