okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
5bdc0ee
main
1"""A cell's output past the preview is spilled whole to a file, and a cell that 2pushes the kernel past its memory cap is interrupted while the namespace lives on.""" 3 4import json 5import os 6import unittest 7 8from harness import Albedo ,Provider ,exclusive ,latest_user ,python ,text 9 10 11LINES = 3000 # ~200 KiB, past the 64 KiB preview and the ascii shortcut 12 13# Grows by 8 MiB every 50 ms until the cap stops it; the chunks stay in the 14# namespace, so the kernel is still past the cap when the next cell starts. 15HOG = ( 16"import time\n" 17"chunks = []\n" 18"while True:\n" 19" chunks.append(bytearray(8 * 1024 * 1024))\n" 20" time.sleep(0.05)\n" 21) 22 23 24# exclusive: the memory cap is daemon environment 25@exclusive 26class CellLimitsTests (unittest .TestCase ): 27def setUp (self ): 28self .results = [] 29 30def script (request ): 31last = request ["messages" ][- 1 ] 32if last .get ("role" )== "tool" : 33self .results .append (json .loads (last ["content" ])) 34return text ("noted" ) 35user = latest_user (request ) 36if "large spill" in user : 37return python ("print('p' * 2097152 + 'the end', end='')" ) 38if "page spill" in user : 39return python ( 40f"id = { self . large_cell !r } \n" 41"assert output.read(id, offset=1048576-2, limit=10) == 'p' * 10\n" 42"assert output.read(id, offset=2097152, limit=65536) == 'the end'\n" 43"assert output.read(id, offset=2097159, limit=65536) == ''\n" 44"print('cell spill pages verified')" 45 ) 46if "read the spill" in user : 47return python ( 48f"print(files.read( { self . spill !r } , start_line= { LINES - 1 } ))" 49 ) 50if "spill" in user : 51return python ( 52f"for i in range( { LINES } ):\n" 53" print(f'line {i:05d} ' + 'x' * 60)\n" 54"print('the last line')" 55 ) 56if "hog" in user : 57return python (HOG ) 58if "free" in user : 59return python ("import gc\ndel chunks\ngc.collect()\nprint('freed')" ) 60return text ("done" ) 61 62def prepare (app ): 63app .env ["ALBEDO_KERNEL_MEMORY_BYTES" ]= str (96 * 1024 * 1024 ) 64 65self .provider = Provider (script ) 66self .addCleanup (self .provider .close ) 67self .app = Albedo (self .provider ,prepare = prepare ) 68self .app .__enter__ () 69self .addCleanup (self .app .__exit__ ,None ,None ,None ) 70self .session = self .app .session () 71 72def cell (self ,prompt ): 73self .app .prompt (self .session ,prompt ).close () 74self .app .idle (self .session ) 75return self .results [- 1 ] 76 77def test_output_past_the_preview_is_spilled_whole (self ): 78result = self .cell ("spill" ) 79self .assertEqual (result ["status" ],"ok" ) 80self .assertTrue (result ["truncated" ]) 81self .assertIn ("line 00000" ,result ["output" ]) 82self .assertNotIn ("the last line" ,result ["output" ].split ("[output was" )[0 ]) 83note = result ["output" ].rsplit ("[output was" ,1 )[1 ] 84self .spill = note .split (" in " ,1 )[1 ].split (":" )[0 ] 85self .assertEqual ( 86os .path .dirname (self .spill ),str (self .app .home / "output" ),self .spill 87 ) 88with open (self .spill ,"rb" )as spilled : 89content = spilled .read () 90self .assertTrue (content .startswith (b"line 00000 xxx" ),content [:40 ]) 91self .assertTrue (content .endswith (b"the last line\n" ),content [- 40 :]) 92self .assertTrue ( 93note .startswith (f" { len ( content ) } bytes; all of it is in" ),note 94 ) 95read = self .cell ("read the spill" ) 96self .assertIn (f"line { LINES - 1 :05d } " ,read ["output" ]) 97self .assertIn ("the last line" ,read ["output" ]) 98 99def test_cell_output_pages_its_spill_past_the_memory_prefix (self ): 100spilled = self .cell ("large spill" ) 101self .assertEqual (spilled ["status" ],"ok" ,spilled ) 102self .large_cell = spilled ["cell_id" ] 103result = self .cell ("page spill" ) 104self .assertEqual (result ["status" ],"ok" ,result ) 105self .assertIn ("cell spill pages verified" ,result ["output" ]) 106 107def test_a_cell_past_the_memory_cap_is_interrupted_and_the_namespace_kept (self ): 108hog = self .cell ("hog" ) 109self .assertEqual (hog ["status" ],"interrupted" ,hog ) 110self .assertIn ("kernel memory passed its cap" ,hog ["output" ]) 111self .assertIn ("cap 96 MiB" ,hog ["output" ]) 112# Over the cap already, a cell that frees memory still runs. 113freed = self .cell ("free" ) 114self .assertEqual (freed ["status" ],"ok" ,freed ) 115self .assertEqual (freed ["output" ].strip (),"freed" ) 116# The memory really went: the next hog is stopped at the cap again. 117again = self .cell ("hog" ) 118self .assertEqual (again ["status" ],"interrupted" ,again ) 119self .assertIn ("kernel memory passed its cap" ,again ["output" ])