Skip to content

Commit 2bdc03d

Browse files
Add test to check spice kernels are only loaded once and cleared
1 parent cca336e commit 2bdc03d

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from Basilisk.utilities import SimulationBaseClass
2+
from Basilisk.simulation import spiceInterface
3+
4+
from Basilisk import __path__
5+
bskPath = __path__[0]
6+
7+
8+
def createOneSim():
9+
"""
10+
Create a minimal Basilisk simulation containing a single SpiceInterface.
11+
12+
Returns
13+
-------
14+
TotalSim : Basilisk SimulationBaseClass instance
15+
The newly created simulation object.
16+
SpiceObject : SpiceInterface
17+
The SpiceInterface instance inside the created simulation.
18+
"""
19+
TotalSim = SimulationBaseClass.SimBaseClass()
20+
DynUnitTestProc = TotalSim.CreateNewProcess("process")
21+
DynUnitTestProc.addTask(TotalSim.CreateNewTask("task", 1))
22+
23+
# Create and register the SpiceInterface
24+
SpiceObject = spiceInterface.SpiceInterface()
25+
SpiceObject.SPICEDataPath = bskPath + '/supportData/EphemerisData/'
26+
TotalSim.AddModelToTask("task", SpiceObject)
27+
28+
# Run long enough for the SpiceInterface to furnish its kernels
29+
TotalSim.ConfigureStopTime(2)
30+
TotalSim.InitializeSimulation()
31+
TotalSim.ExecuteSimulation()
32+
33+
return TotalSim, SpiceObject
34+
35+
36+
def test_multipleInterfaces():
37+
"""
38+
Verify that SPICE kernels loaded through SpiceInterface are correctly
39+
reference-counted and unloaded when all SpiceInterface instances are gone.
40+
41+
The test performs the following high-level checks:
42+
43+
1. Before creating any SpiceInterface objects, the target kernel must not
44+
be loaded in SPICE.
45+
46+
2. Creating the first simulation should cause the kernel to be furnished.
47+
48+
3. Creating many additional simulations must *not* load the kernel again.
49+
50+
4. After all simulations have loaded, the number of loaded kernels should
51+
be the same as after loading one sim.
52+
53+
5. After all simulations fall out of scope and Python's garbage collector
54+
runs, the kernel must be fully unloaded from SPICE.
55+
56+
This guarantees that:
57+
- furnsh_c() is only called once per unique kernel file
58+
- unload_c() is only called when the last user disappears
59+
- the shared-pointer-based lifetime system works correctly
60+
"""
61+
kernel = f"{bskPath}/supportData/EphemerisData/de430.bsp"
62+
63+
# Step 1 - Kernel not yet loaded
64+
assert not spiceInterface.isKernelLoaded(kernel)
65+
66+
def smallScope():
67+
# Step 2 - First SpiceInterface loads the kernel
68+
firstSim, firstSpice = createOneSim()
69+
assert spiceInterface.isKernelLoaded(kernel)
70+
71+
kernelsLoadedWithOneSim = spiceInterface.countKernelsLoaded()
72+
73+
# Step 3 - Many more SpiceInterfaces do NOT reload the kernel
74+
cacheSims = []
75+
N = 20
76+
for _ in range(N):
77+
cacheSims.append(createOneSim())
78+
79+
kernelsLoadedWithNSims = spiceInterface.countKernelsLoaded()
80+
81+
# Step 4 - check kernels are not being loaded again
82+
assert kernelsLoadedWithOneSim == kernelsLoadedWithNSims
83+
84+
# sanity check kernel is still loaded
85+
assert spiceInterface.isKernelLoaded(kernel)
86+
87+
# Everything in smallScope is destroyed once we leave the function
88+
smallScope()
89+
90+
import gc
91+
gc.collect()
92+
93+
# Step 5 - Kernel must now be fully unloaded
94+
assert not spiceInterface.isKernelLoaded(kernel)
95+
96+
97+
if __name__ == "__main__":
98+
test_multipleInterfaces()

0 commit comments

Comments
 (0)