11package soltestutils
22
33import (
4- "io"
5- "os"
4+ "maps"
65 "path/filepath"
6+ "sync"
77 "testing"
88
99 "github.com/stretchr/testify/require"
@@ -13,71 +13,76 @@ import (
1313 "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/solutils"
1414)
1515
16- // LoadMCMSPrograms loads the MCMS program artifacts into the given directory.
17- //
18- // Returns the path to the temporary test directory and a map of program names to IDs.
19- func LoadMCMSPrograms (t * testing.T , dir string ) (string , map [string ]string ) {
20- t .Helper ()
16+ // solTestExclusive serializes top-level Solana integration tests that mutate global
17+ // gobinding program IDs via SetProgramID. solTestDepth allows nested subtests in
18+ // the same test to re-enter without deadlocking.
19+ var (
20+ solTestExclusive sync.Mutex
21+ solTestCountMu sync.Mutex
22+ solTestDepth int
23+ )
2124
22- progIDs := loadProgramArtifacts (t ,
23- solutils .MCMSProgramNames , downloadChainlinkCCIPProgramArtifacts , dir ,
24- )
25+ func acquireSolanaTestIsolation (t * testing.T ) {
26+ t .Helper ()
2527
26- return dir , progIDs
28+ solTestCountMu .Lock ()
29+ if solTestDepth == 0 {
30+ solTestExclusive .Lock ()
31+ }
32+ solTestDepth ++
33+ solTestCountMu .Unlock ()
34+
35+ t .Cleanup (func () {
36+ solTestCountMu .Lock ()
37+ solTestDepth --
38+ release := solTestDepth == 0
39+ solTestCountMu .Unlock ()
40+ if release {
41+ solTestExclusive .Unlock ()
42+ }
43+ })
2744}
2845
29- // PreloadMCMS provides a convenience function to preload the MCMS program artifacts and address
30- // book for a given selector.
31- func PreloadMCMS (t * testing.T , selector uint64 ) (string , map [string ]string , * cldf.AddressBookMap ) {
46+ var (
47+ mcmsProgramsOnce sync.Once
48+ mcmsProgramsPath string
49+ mcmsProgramIDs map [string ]string
50+ )
51+
52+ // sharedMCMSPrograms downloads MCMS Solana program artifacts once per test process
53+ // and returns the shared cache directory plus program IDs.
54+ func sharedMCMSPrograms (t * testing.T ) (string , map [string ]string ) {
3255 t .Helper ()
3356
34- dir := t .TempDir ()
57+ mcmsProgramsOnce .Do (func () {
58+ mcmsProgramsPath = programsCacheDir ()
59+ err := solutils .DownloadChainlinkCCIPProgramArtifacts (t .Context (), mcmsProgramsPath , "" , nil )
60+ require .NoError (t , err )
3561
36- _ , programIDs := LoadMCMSPrograms (t , dir )
62+ mcmsProgramIDs = make (map [string ]string , len (solutils .MCMSProgramNames ))
63+ for _ , name := range solutils .MCMSProgramNames {
64+ id := solutils .GetProgramID (name )
65+ require .NotEmpty (t , id , "program id not found for program name: %s" , name )
66+ require .FileExists (t , filepath .Join (mcmsProgramsPath , name + ".so" ))
67+ mcmsProgramIDs [name ] = id
68+ }
69+ })
3770
38- ab := PreloadAddressBookWithMCMSPrograms (t , selector )
71+ programIDs := make (map [string ]string , len (mcmsProgramIDs ))
72+ maps .Copy (programIDs , mcmsProgramIDs )
3973
40- return dir , programIDs , ab
74+ return mcmsProgramsPath , programIDs
4175}
4276
43- // loadProgramArtifacts is a helper function that loads program artifacts into a temporary test directory.
44- // It downloads artifacts using the provided download function and copies the specified programs.
45- //
46- // Returns the map of program names to IDs.
47- func loadProgramArtifacts (t * testing.T , programNames []string , downloadFn downloadFunc , targetDir string ) map [string ]string {
77+ // PreloadMCMS provides a convenience function to preload the MCMS program artifacts and address
78+ // book for a given selector.
79+ func PreloadMCMS (t * testing.T , selector uint64 ) (string , map [string ]string , * cldf.AddressBookMap ) {
4880 t .Helper ()
4981
50- // Download the program artifacts using the provided download function
51- cachePath := downloadFn (t )
52-
53- progIDs := make (map [string ]string , len (programNames ))
54-
55- // Copy the specific artifacts to the target directory and add the program ID to the map
56- for _ , name := range programNames {
57- id := solutils .GetProgramID (name )
58- require .NotEmpty (t , id , "program id not found for program name: %s" , name )
59-
60- src := filepath .Join (cachePath , name + ".so" )
61- dst := filepath .Join (targetDir , name + ".so" )
82+ acquireSolanaTestIsolation (t )
6283
63- func () {
64- srcFile , err := os .Open (src )
65- require .NoError (t , err )
66- defer srcFile .Close ()
67-
68- dstFile , err := os .Create (dst )
69- require .NoError (t , err )
70- defer dstFile .Close ()
71-
72- _ , err = io .Copy (dstFile , srcFile )
73- require .NoError (t , err )
74- }()
75-
76- // Add the program ID to the map
77- progIDs [name ] = id
78- t .Logf ("copied solana program %s to %s" , name , dst )
79- }
84+ programsPath , programIDs := sharedMCMSPrograms (t )
85+ ab := PreloadAddressBookWithMCMSPrograms (t , selector )
8086
81- // Return the path to the cached artifacts and the map of program IDs
82- return progIDs
87+ return programsPath , programIDs , ab
8388}
0 commit comments