11package soltestutils
22
33import (
4- "io"
5- "os"
64 "path/filepath"
5+ "sync"
76 "testing"
87
98 "github.com/stretchr/testify/require"
@@ -13,71 +12,82 @@ import (
1312 "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/solutils"
1413)
1514
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 ()
15+ // solTestExclusive serializes top-level Solana integration tests that mutate global
16+ // gobinding program IDs via SetProgramID. solTestDepth allows nested subtests in
17+ // the same test to re-enter without deadlocking.
18+ var (
19+ solTestExclusive sync.Mutex
20+ solTestCountMu sync.Mutex
21+ solTestDepth int
22+ )
2123
22- progIDs := loadProgramArtifacts (t ,
23- solutils .MCMSProgramNames , downloadChainlinkCCIPProgramArtifacts , dir ,
24- )
24+ func acquireSolanaTestIsolation (t * testing.T ) {
25+ t .Helper ()
2526
26- return dir , progIDs
27+ solTestCountMu .Lock ()
28+ if solTestDepth == 0 {
29+ solTestExclusive .Lock ()
30+ }
31+ solTestDepth ++
32+ solTestCountMu .Unlock ()
33+
34+ t .Cleanup (func () {
35+ solTestCountMu .Lock ()
36+ solTestDepth --
37+ release := solTestDepth == 0
38+ solTestCountMu .Unlock ()
39+ if release {
40+ solTestExclusive .Unlock ()
41+ }
42+ })
2743}
2844
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 ) {
32- t .Helper ()
45+ var (
46+ mcmsProgramsOnce sync.Once
47+ mcmsProgramsPath string
48+ mcmsProgramIDs map [string ]string
49+ )
3350
34- dir := t .TempDir ()
51+ // sharedMCMSPrograms downloads MCMS Solana program artifacts once per test process
52+ // and returns the shared cache directory plus program IDs.
53+ func sharedMCMSPrograms (t * testing.T ) (string , map [string ]string ) {
54+ t .Helper ()
3555
36- _ , programIDs := LoadMCMSPrograms (t , dir )
56+ mcmsProgramsOnce .Do (func () {
57+ mcmsProgramsPath = programsCacheDir ()
58+ err := solutils .DownloadChainlinkCCIPProgramArtifacts (t .Context (), mcmsProgramsPath , "" , nil )
59+ require .NoError (t , err )
60+
61+ mcmsProgramIDs = make (map [string ]string , len (solutils .MCMSProgramNames ))
62+ for _ , name := range solutils .MCMSProgramNames {
63+ id := solutils .GetProgramID (name )
64+ require .NotEmpty (t , id , "program id not found for program name: %s" , name )
65+ require .FileExists (t , filepath .Join (mcmsProgramsPath , name + ".so" ))
66+ mcmsProgramIDs [name ] = id
67+ }
68+ })
69+
70+ return mcmsProgramsPath , copyProgramIDs (mcmsProgramIDs )
71+ }
3772
38- ab := PreloadAddressBookWithMCMSPrograms (t , selector )
73+ func copyProgramIDs (src map [string ]string ) map [string ]string {
74+ dst := make (map [string ]string , len (src ))
75+ for name , id := range src {
76+ dst [name ] = id
77+ }
3978
40- return dir , programIDs , ab
79+ return dst
4180}
4281
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 {
82+ // PreloadMCMS provides a convenience function to preload the MCMS program artifacts and address
83+ // book for a given selector.
84+ func PreloadMCMS (t * testing.T , selector uint64 ) (string , map [string ]string , * cldf.AddressBookMap ) {
4885 t .Helper ()
4986
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 )
87+ acquireSolanaTestIsolation (t )
5988
60- src := filepath .Join (cachePath , name + ".so" )
61- dst := filepath .Join (targetDir , name + ".so" )
62-
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- }
89+ programsPath , programIDs := sharedMCMSPrograms (t )
90+ ab := PreloadAddressBookWithMCMSPrograms (t , selector )
8091
81- // Return the path to the cached artifacts and the map of program IDs
82- return progIDs
92+ return programsPath , programIDs , ab
8393}
0 commit comments