feat(webkit): add low-level performance tuning infrastructure
- commit
- cd437e3cc74fc2213e06d77a13fae5253f79afeb
- author
- brice · · 2025-12-28 21:21 UTC
- committer
- brice · · 2025-12-29 17:28 UTC
- parent
- a85da87
- signature
- unsigned
9 files · +1068 -9
M .mockery.yaml +2 -0
| @@ -18,6 +18,8 @@ packages: | |||
| 18 | 18 | WebView: {} | |
| 19 | 19 | WebViewFactory: {} | |
| 20 | 20 | WebViewPool: {} | |
| 21 | + | MemoryPressureApplier: {} | |
| 22 | + | WebKitContextProvider: {} | |
| 21 | 23 | github.com/bnema/dumber/internal/domain/repository: | |
| 22 | 24 | config: | |
| 23 | 25 | dir: '{{.InterfaceDir}}/mocks' | |
| @@ -18,6 +18,8 @@ packages: | |||||
| 18 | WebView: {} | 18 | WebView: {} | ||
| 19 | WebViewFactory: {} | 19 | WebViewFactory: {} | ||
| 20 | WebViewPool: {} | 20 | WebViewPool: {} | ||
| 21 | + | MemoryPressureApplier: {} | |||
| 22 | + | WebKitContextProvider: {} | |||
| 21 | github.com/bnema/dumber/internal/domain/repository: | 23 | github.com/bnema/dumber/internal/domain/repository: | ||
| 22 | config: | 24 | config: | ||
| 23 | dir: '{{.InterfaceDir}}/mocks' | 25 | dir: '{{.InterfaceDir}}/mocks' | ||
M internal/application/port/mocks/mocks.go +408 -0
| @@ -940,6 +940,414 @@ func (_c *MockSessionSpawner_SpawnWithSession_Call) RunAndReturn(run func(sessio | |||
| 940 | 940 | return _c | |
| 941 | 941 | } | |
| 942 | 942 | ||
| 943 | + | // NewMockMemoryPressureApplier creates a new instance of MockMemoryPressureApplier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 944 | + | // The first argument is typically a *testing.T value. | |
| 945 | + | func NewMockMemoryPressureApplier(t interface { | |
| 946 | + | mock.TestingT | |
| 947 | + | Cleanup(func()) | |
| 948 | + | }) *MockMemoryPressureApplier { | |
| 949 | + | mock := &MockMemoryPressureApplier{} | |
| 950 | + | mock.Mock.Test(t) | |
| 951 | + | ||
| 952 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 953 | + | ||
| 954 | + | return mock | |
| 955 | + | } | |
| 956 | + | ||
| 957 | + | // MockMemoryPressureApplier is an autogenerated mock type for the MemoryPressureApplier type | |
| 958 | + | type MockMemoryPressureApplier struct { | |
| 959 | + | mock.Mock | |
| 960 | + | } | |
| 961 | + | ||
| 962 | + | type MockMemoryPressureApplier_Expecter struct { | |
| 963 | + | mock *mock.Mock | |
| 964 | + | } | |
| 965 | + | ||
| 966 | + | func (_m *MockMemoryPressureApplier) EXPECT() *MockMemoryPressureApplier_Expecter { | |
| 967 | + | return &MockMemoryPressureApplier_Expecter{mock: &_m.Mock} | |
| 968 | + | } | |
| 969 | + | ||
| 970 | + | // ApplyNetworkProcessSettings provides a mock function for the type MockMemoryPressureApplier | |
| 971 | + | func (_mock *MockMemoryPressureApplier) ApplyNetworkProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) error { | |
| 972 | + | ret := _mock.Called(ctx, cfg) | |
| 973 | + | ||
| 974 | + | if len(ret) == 0 { | |
| 975 | + | panic("no return value specified for ApplyNetworkProcessSettings") | |
| 976 | + | } | |
| 977 | + | ||
| 978 | + | var r0 error | |
| 979 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) error); ok { | |
| 980 | + | r0 = returnFunc(ctx, cfg) | |
| 981 | + | } else { | |
| 982 | + | r0 = ret.Error(0) | |
| 983 | + | } | |
| 984 | + | return r0 | |
| 985 | + | } | |
| 986 | + | ||
| 987 | + | // MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyNetworkProcessSettings' | |
| 988 | + | type MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call struct { | |
| 989 | + | *mock.Call | |
| 990 | + | } | |
| 991 | + | ||
| 992 | + | // ApplyNetworkProcessSettings is a helper method to define mock.On call | |
| 993 | + | // - ctx context.Context | |
| 994 | + | // - cfg *port.MemoryPressureConfig | |
| 995 | + | func (_e *MockMemoryPressureApplier_Expecter) ApplyNetworkProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call { | |
| 996 | + | return &MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call{Call: _e.mock.On("ApplyNetworkProcessSettings", ctx, cfg)} | |
| 997 | + | } | |
| 998 | + | ||
| 999 | + | func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call { | |
| 1000 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1001 | + | var arg0 context.Context | |
| 1002 | + | if args[0] != nil { | |
| 1003 | + | arg0 = args[0].(context.Context) | |
| 1004 | + | } | |
| 1005 | + | var arg1 *port.MemoryPressureConfig | |
| 1006 | + | if args[1] != nil { | |
| 1007 | + | arg1 = args[1].(*port.MemoryPressureConfig) | |
| 1008 | + | } | |
| 1009 | + | run( | |
| 1010 | + | arg0, | |
| 1011 | + | arg1, | |
| 1012 | + | ) | |
| 1013 | + | }) | |
| 1014 | + | return _c | |
| 1015 | + | } | |
| 1016 | + | ||
| 1017 | + | func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Return(err error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call { | |
| 1018 | + | _c.Call.Return(err) | |
| 1019 | + | return _c | |
| 1020 | + | } | |
| 1021 | + | ||
| 1022 | + | func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call { | |
| 1023 | + | _c.Call.Return(run) | |
| 1024 | + | return _c | |
| 1025 | + | } | |
| 1026 | + | ||
| 1027 | + | // ApplyWebProcessSettings provides a mock function for the type MockMemoryPressureApplier | |
| 1028 | + | func (_mock *MockMemoryPressureApplier) ApplyWebProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error) { | |
| 1029 | + | ret := _mock.Called(ctx, cfg) | |
| 1030 | + | ||
| 1031 | + | if len(ret) == 0 { | |
| 1032 | + | panic("no return value specified for ApplyWebProcessSettings") | |
| 1033 | + | } | |
| 1034 | + | ||
| 1035 | + | var r0 any | |
| 1036 | + | var r1 error | |
| 1037 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) (any, error)); ok { | |
| 1038 | + | return returnFunc(ctx, cfg) | |
| 1039 | + | } | |
| 1040 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) any); ok { | |
| 1041 | + | r0 = returnFunc(ctx, cfg) | |
| 1042 | + | } else { | |
| 1043 | + | if ret.Get(0) != nil { | |
| 1044 | + | r0 = ret.Get(0).(any) | |
| 1045 | + | } | |
| 1046 | + | } | |
| 1047 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, *port.MemoryPressureConfig) error); ok { | |
| 1048 | + | r1 = returnFunc(ctx, cfg) | |
| 1049 | + | } else { | |
| 1050 | + | r1 = ret.Error(1) | |
| 1051 | + | } | |
| 1052 | + | return r0, r1 | |
| 1053 | + | } | |
| 1054 | + | ||
| 1055 | + | // MockMemoryPressureApplier_ApplyWebProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyWebProcessSettings' | |
| 1056 | + | type MockMemoryPressureApplier_ApplyWebProcessSettings_Call struct { | |
| 1057 | + | *mock.Call | |
| 1058 | + | } | |
| 1059 | + | ||
| 1060 | + | // ApplyWebProcessSettings is a helper method to define mock.On call | |
| 1061 | + | // - ctx context.Context | |
| 1062 | + | // - cfg *port.MemoryPressureConfig | |
| 1063 | + | func (_e *MockMemoryPressureApplier_Expecter) ApplyWebProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call { | |
| 1064 | + | return &MockMemoryPressureApplier_ApplyWebProcessSettings_Call{Call: _e.mock.On("ApplyWebProcessSettings", ctx, cfg)} | |
| 1065 | + | } | |
| 1066 | + | ||
| 1067 | + | func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call { | |
| 1068 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1069 | + | var arg0 context.Context | |
| 1070 | + | if args[0] != nil { | |
| 1071 | + | arg0 = args[0].(context.Context) | |
| 1072 | + | } | |
| 1073 | + | var arg1 *port.MemoryPressureConfig | |
| 1074 | + | if args[1] != nil { | |
| 1075 | + | arg1 = args[1].(*port.MemoryPressureConfig) | |
| 1076 | + | } | |
| 1077 | + | run( | |
| 1078 | + | arg0, | |
| 1079 | + | arg1, | |
| 1080 | + | ) | |
| 1081 | + | }) | |
| 1082 | + | return _c | |
| 1083 | + | } | |
| 1084 | + | ||
| 1085 | + | func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Return(v any, err error) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call { | |
| 1086 | + | _c.Call.Return(v, err) | |
| 1087 | + | return _c | |
| 1088 | + | } | |
| 1089 | + | ||
| 1090 | + | func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call { | |
| 1091 | + | _c.Call.Return(run) | |
| 1092 | + | return _c | |
| 1093 | + | } | |
| 1094 | + | ||
| 1095 | + | // NewMockWebKitContextProvider creates a new instance of MockWebKitContextProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 1096 | + | // The first argument is typically a *testing.T value. | |
| 1097 | + | func NewMockWebKitContextProvider(t interface { | |
| 1098 | + | mock.TestingT | |
| 1099 | + | Cleanup(func()) | |
| 1100 | + | }) *MockWebKitContextProvider { | |
| 1101 | + | mock := &MockWebKitContextProvider{} | |
| 1102 | + | mock.Mock.Test(t) | |
| 1103 | + | ||
| 1104 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 1105 | + | ||
| 1106 | + | return mock | |
| 1107 | + | } | |
| 1108 | + | ||
| 1109 | + | // MockWebKitContextProvider is an autogenerated mock type for the WebKitContextProvider type | |
| 1110 | + | type MockWebKitContextProvider struct { | |
| 1111 | + | mock.Mock | |
| 1112 | + | } | |
| 1113 | + | ||
| 1114 | + | type MockWebKitContextProvider_Expecter struct { | |
| 1115 | + | mock *mock.Mock | |
| 1116 | + | } | |
| 1117 | + | ||
| 1118 | + | func (_m *MockWebKitContextProvider) EXPECT() *MockWebKitContextProvider_Expecter { | |
| 1119 | + | return &MockWebKitContextProvider_Expecter{mock: &_m.Mock} | |
| 1120 | + | } | |
| 1121 | + | ||
| 1122 | + | // CacheDir provides a mock function for the type MockWebKitContextProvider | |
| 1123 | + | func (_mock *MockWebKitContextProvider) CacheDir() string { | |
| 1124 | + | ret := _mock.Called() | |
| 1125 | + | ||
| 1126 | + | if len(ret) == 0 { | |
| 1127 | + | panic("no return value specified for CacheDir") | |
| 1128 | + | } | |
| 1129 | + | ||
| 1130 | + | var r0 string | |
| 1131 | + | if returnFunc, ok := ret.Get(0).(func() string); ok { | |
| 1132 | + | r0 = returnFunc() | |
| 1133 | + | } else { | |
| 1134 | + | r0 = ret.Get(0).(string) | |
| 1135 | + | } | |
| 1136 | + | return r0 | |
| 1137 | + | } | |
| 1138 | + | ||
| 1139 | + | // MockWebKitContextProvider_CacheDir_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CacheDir' | |
| 1140 | + | type MockWebKitContextProvider_CacheDir_Call struct { | |
| 1141 | + | *mock.Call | |
| 1142 | + | } | |
| 1143 | + | ||
| 1144 | + | // CacheDir is a helper method to define mock.On call | |
| 1145 | + | func (_e *MockWebKitContextProvider_Expecter) CacheDir() *MockWebKitContextProvider_CacheDir_Call { | |
| 1146 | + | return &MockWebKitContextProvider_CacheDir_Call{Call: _e.mock.On("CacheDir")} | |
| 1147 | + | } | |
| 1148 | + | ||
| 1149 | + | func (_c *MockWebKitContextProvider_CacheDir_Call) Run(run func()) *MockWebKitContextProvider_CacheDir_Call { | |
| 1150 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1151 | + | run() | |
| 1152 | + | }) | |
| 1153 | + | return _c | |
| 1154 | + | } | |
| 1155 | + | ||
| 1156 | + | func (_c *MockWebKitContextProvider_CacheDir_Call) Return(s string) *MockWebKitContextProvider_CacheDir_Call { | |
| 1157 | + | _c.Call.Return(s) | |
| 1158 | + | return _c | |
| 1159 | + | } | |
| 1160 | + | ||
| 1161 | + | func (_c *MockWebKitContextProvider_CacheDir_Call) RunAndReturn(run func() string) *MockWebKitContextProvider_CacheDir_Call { | |
| 1162 | + | _c.Call.Return(run) | |
| 1163 | + | return _c | |
| 1164 | + | } | |
| 1165 | + | ||
| 1166 | + | // Close provides a mock function for the type MockWebKitContextProvider | |
| 1167 | + | func (_mock *MockWebKitContextProvider) Close() error { | |
| 1168 | + | ret := _mock.Called() | |
| 1169 | + | ||
| 1170 | + | if len(ret) == 0 { | |
| 1171 | + | panic("no return value specified for Close") | |
| 1172 | + | } | |
| 1173 | + | ||
| 1174 | + | var r0 error | |
| 1175 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |
| 1176 | + | r0 = returnFunc() | |
| 1177 | + | } else { | |
| 1178 | + | r0 = ret.Error(0) | |
| 1179 | + | } | |
| 1180 | + | return r0 | |
| 1181 | + | } | |
| 1182 | + | ||
| 1183 | + | // MockWebKitContextProvider_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' | |
| 1184 | + | type MockWebKitContextProvider_Close_Call struct { | |
| 1185 | + | *mock.Call | |
| 1186 | + | } | |
| 1187 | + | ||
| 1188 | + | // Close is a helper method to define mock.On call | |
| 1189 | + | func (_e *MockWebKitContextProvider_Expecter) Close() *MockWebKitContextProvider_Close_Call { | |
| 1190 | + | return &MockWebKitContextProvider_Close_Call{Call: _e.mock.On("Close")} | |
| 1191 | + | } | |
| 1192 | + | ||
| 1193 | + | func (_c *MockWebKitContextProvider_Close_Call) Run(run func()) *MockWebKitContextProvider_Close_Call { | |
| 1194 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1195 | + | run() | |
| 1196 | + | }) | |
| 1197 | + | return _c | |
| 1198 | + | } | |
| 1199 | + | ||
| 1200 | + | func (_c *MockWebKitContextProvider_Close_Call) Return(err error) *MockWebKitContextProvider_Close_Call { | |
| 1201 | + | _c.Call.Return(err) | |
| 1202 | + | return _c | |
| 1203 | + | } | |
| 1204 | + | ||
| 1205 | + | func (_c *MockWebKitContextProvider_Close_Call) RunAndReturn(run func() error) *MockWebKitContextProvider_Close_Call { | |
| 1206 | + | _c.Call.Return(run) | |
| 1207 | + | return _c | |
| 1208 | + | } | |
| 1209 | + | ||
| 1210 | + | // DataDir provides a mock function for the type MockWebKitContextProvider | |
| 1211 | + | func (_mock *MockWebKitContextProvider) DataDir() string { | |
| 1212 | + | ret := _mock.Called() | |
| 1213 | + | ||
| 1214 | + | if len(ret) == 0 { | |
| 1215 | + | panic("no return value specified for DataDir") | |
| 1216 | + | } | |
| 1217 | + | ||
| 1218 | + | var r0 string | |
| 1219 | + | if returnFunc, ok := ret.Get(0).(func() string); ok { | |
| 1220 | + | r0 = returnFunc() | |
| 1221 | + | } else { | |
| 1222 | + | r0 = ret.Get(0).(string) | |
| 1223 | + | } | |
| 1224 | + | return r0 | |
| 1225 | + | } | |
| 1226 | + | ||
| 1227 | + | // MockWebKitContextProvider_DataDir_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DataDir' | |
| 1228 | + | type MockWebKitContextProvider_DataDir_Call struct { | |
| 1229 | + | *mock.Call | |
| 1230 | + | } | |
| 1231 | + | ||
| 1232 | + | // DataDir is a helper method to define mock.On call | |
| 1233 | + | func (_e *MockWebKitContextProvider_Expecter) DataDir() *MockWebKitContextProvider_DataDir_Call { | |
| 1234 | + | return &MockWebKitContextProvider_DataDir_Call{Call: _e.mock.On("DataDir")} | |
| 1235 | + | } | |
| 1236 | + | ||
| 1237 | + | func (_c *MockWebKitContextProvider_DataDir_Call) Run(run func()) *MockWebKitContextProvider_DataDir_Call { | |
| 1238 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1239 | + | run() | |
| 1240 | + | }) | |
| 1241 | + | return _c | |
| 1242 | + | } | |
| 1243 | + | ||
| 1244 | + | func (_c *MockWebKitContextProvider_DataDir_Call) Return(s string) *MockWebKitContextProvider_DataDir_Call { | |
| 1245 | + | _c.Call.Return(s) | |
| 1246 | + | return _c | |
| 1247 | + | } | |
| 1248 | + | ||
| 1249 | + | func (_c *MockWebKitContextProvider_DataDir_Call) RunAndReturn(run func() string) *MockWebKitContextProvider_DataDir_Call { | |
| 1250 | + | _c.Call.Return(run) | |
| 1251 | + | return _c | |
| 1252 | + | } | |
| 1253 | + | ||
| 1254 | + | // Initialize provides a mock function for the type MockWebKitContextProvider | |
| 1255 | + | func (_mock *MockWebKitContextProvider) Initialize(ctx context.Context, opts port.WebKitContextOptions) error { | |
| 1256 | + | ret := _mock.Called(ctx, opts) | |
| 1257 | + | ||
| 1258 | + | if len(ret) == 0 { | |
| 1259 | + | panic("no return value specified for Initialize") | |
| 1260 | + | } | |
| 1261 | + | ||
| 1262 | + | var r0 error | |
| 1263 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, port.WebKitContextOptions) error); ok { | |
| 1264 | + | r0 = returnFunc(ctx, opts) | |
| 1265 | + | } else { | |
| 1266 | + | r0 = ret.Error(0) | |
| 1267 | + | } | |
| 1268 | + | return r0 | |
| 1269 | + | } | |
| 1270 | + | ||
| 1271 | + | // MockWebKitContextProvider_Initialize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Initialize' | |
| 1272 | + | type MockWebKitContextProvider_Initialize_Call struct { | |
| 1273 | + | *mock.Call | |
| 1274 | + | } | |
| 1275 | + | ||
| 1276 | + | // Initialize is a helper method to define mock.On call | |
| 1277 | + | // - ctx context.Context | |
| 1278 | + | // - opts port.WebKitContextOptions | |
| 1279 | + | func (_e *MockWebKitContextProvider_Expecter) Initialize(ctx interface{}, opts interface{}) *MockWebKitContextProvider_Initialize_Call { | |
| 1280 | + | return &MockWebKitContextProvider_Initialize_Call{Call: _e.mock.On("Initialize", ctx, opts)} | |
| 1281 | + | } | |
| 1282 | + | ||
| 1283 | + | func (_c *MockWebKitContextProvider_Initialize_Call) Run(run func(ctx context.Context, opts port.WebKitContextOptions)) *MockWebKitContextProvider_Initialize_Call { | |
| 1284 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1285 | + | var arg0 context.Context | |
| 1286 | + | if args[0] != nil { | |
| 1287 | + | arg0 = args[0].(context.Context) | |
| 1288 | + | } | |
| 1289 | + | var arg1 port.WebKitContextOptions | |
| 1290 | + | if args[1] != nil { | |
| 1291 | + | arg1 = args[1].(port.WebKitContextOptions) | |
| 1292 | + | } | |
| 1293 | + | run( | |
| 1294 | + | arg0, | |
| 1295 | + | arg1, | |
| 1296 | + | ) | |
| 1297 | + | }) | |
| 1298 | + | return _c | |
| 1299 | + | } | |
| 1300 | + | ||
| 1301 | + | func (_c *MockWebKitContextProvider_Initialize_Call) Return(err error) *MockWebKitContextProvider_Initialize_Call { | |
| 1302 | + | _c.Call.Return(err) | |
| 1303 | + | return _c | |
| 1304 | + | } | |
| 1305 | + | ||
| 1306 | + | func (_c *MockWebKitContextProvider_Initialize_Call) RunAndReturn(run func(ctx context.Context, opts port.WebKitContextOptions) error) *MockWebKitContextProvider_Initialize_Call { | |
| 1307 | + | _c.Call.Return(run) | |
| 1308 | + | return _c | |
| 1309 | + | } | |
| 1310 | + | ||
| 1311 | + | // PrefetchDNS provides a mock function for the type MockWebKitContextProvider | |
| 1312 | + | func (_mock *MockWebKitContextProvider) PrefetchDNS(hostname string) { | |
| 1313 | + | _mock.Called(hostname) | |
| 1314 | + | return | |
| 1315 | + | } | |
| 1316 | + | ||
| 1317 | + | // MockWebKitContextProvider_PrefetchDNS_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrefetchDNS' | |
| 1318 | + | type MockWebKitContextProvider_PrefetchDNS_Call struct { | |
| 1319 | + | *mock.Call | |
| 1320 | + | } | |
| 1321 | + | ||
| 1322 | + | // PrefetchDNS is a helper method to define mock.On call | |
| 1323 | + | // - hostname string | |
| 1324 | + | func (_e *MockWebKitContextProvider_Expecter) PrefetchDNS(hostname interface{}) *MockWebKitContextProvider_PrefetchDNS_Call { | |
| 1325 | + | return &MockWebKitContextProvider_PrefetchDNS_Call{Call: _e.mock.On("PrefetchDNS", hostname)} | |
| 1326 | + | } | |
| 1327 | + | ||
| 1328 | + | func (_c *MockWebKitContextProvider_PrefetchDNS_Call) Run(run func(hostname string)) *MockWebKitContextProvider_PrefetchDNS_Call { | |
| 1329 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1330 | + | var arg0 string | |
| 1331 | + | if args[0] != nil { | |
| 1332 | + | arg0 = args[0].(string) | |
| 1333 | + | } | |
| 1334 | + | run( | |
| 1335 | + | arg0, | |
| 1336 | + | ) | |
| 1337 | + | }) | |
| 1338 | + | return _c | |
| 1339 | + | } | |
| 1340 | + | ||
| 1341 | + | func (_c *MockWebKitContextProvider_PrefetchDNS_Call) Return() *MockWebKitContextProvider_PrefetchDNS_Call { | |
| 1342 | + | _c.Call.Return() | |
| 1343 | + | return _c | |
| 1344 | + | } | |
| 1345 | + | ||
| 1346 | + | func (_c *MockWebKitContextProvider_PrefetchDNS_Call) RunAndReturn(run func(hostname string)) *MockWebKitContextProvider_PrefetchDNS_Call { | |
| 1347 | + | _c.Run(run) | |
| 1348 | + | return _c | |
| 1349 | + | } | |
| 1350 | + | ||
| 943 | 1351 | // NewMockWebView creates a new instance of MockWebView. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 944 | 1352 | // The first argument is typically a *testing.T value. | |
| 945 | 1353 | func NewMockWebView(t interface { | |
| @@ -940,6 +940,414 @@ func (_c *MockSessionSpawner_SpawnWithSession_Call) RunAndReturn(run func(sessio | |||||
| 940 | return _c | 940 | return _c | ||
| 941 | } | 941 | } | ||
| 942 | 942 | ||||
| 943 | + | // NewMockMemoryPressureApplier creates a new instance of MockMemoryPressureApplier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |||
| 944 | + | // The first argument is typically a *testing.T value. | |||
| 945 | + | func NewMockMemoryPressureApplier(t interface { | |||
| 946 | + | mock.TestingT | |||
| 947 | + | Cleanup(func()) | |||
| 948 | + | }) *MockMemoryPressureApplier { | |||
| 949 | + | mock := &MockMemoryPressureApplier{} | |||
| 950 | + | mock.Mock.Test(t) | |||
| 951 | + | ||||
| 952 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |||
| 953 | + | ||||
| 954 | + | return mock | |||
| 955 | + | } | |||
| 956 | + | ||||
| 957 | + | // MockMemoryPressureApplier is an autogenerated mock type for the MemoryPressureApplier type | |||
| 958 | + | type MockMemoryPressureApplier struct { | |||
| 959 | + | mock.Mock | |||
| 960 | + | } | |||
| 961 | + | ||||
| 962 | + | type MockMemoryPressureApplier_Expecter struct { | |||
| 963 | + | mock *mock.Mock | |||
| 964 | + | } | |||
| 965 | + | ||||
| 966 | + | func (_m *MockMemoryPressureApplier) EXPECT() *MockMemoryPressureApplier_Expecter { | |||
| 967 | + | return &MockMemoryPressureApplier_Expecter{mock: &_m.Mock} | |||
| 968 | + | } | |||
| 969 | + | ||||
| 970 | + | // ApplyNetworkProcessSettings provides a mock function for the type MockMemoryPressureApplier | |||
| 971 | + | func (_mock *MockMemoryPressureApplier) ApplyNetworkProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) error { | |||
| 972 | + | ret := _mock.Called(ctx, cfg) | |||
| 973 | + | ||||
| 974 | + | if len(ret) == 0 { | |||
| 975 | + | panic("no return value specified for ApplyNetworkProcessSettings") | |||
| 976 | + | } | |||
| 977 | + | ||||
| 978 | + | var r0 error | |||
| 979 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) error); ok { | |||
| 980 | + | r0 = returnFunc(ctx, cfg) | |||
| 981 | + | } else { | |||
| 982 | + | r0 = ret.Error(0) | |||
| 983 | + | } | |||
| 984 | + | return r0 | |||
| 985 | + | } | |||
| 986 | + | ||||
| 987 | + | // MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyNetworkProcessSettings' | |||
| 988 | + | type MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call struct { | |||
| 989 | + | *mock.Call | |||
| 990 | + | } | |||
| 991 | + | ||||
| 992 | + | // ApplyNetworkProcessSettings is a helper method to define mock.On call | |||
| 993 | + | // - ctx context.Context | |||
| 994 | + | // - cfg *port.MemoryPressureConfig | |||
| 995 | + | func (_e *MockMemoryPressureApplier_Expecter) ApplyNetworkProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call { | |||
| 996 | + | return &MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call{Call: _e.mock.On("ApplyNetworkProcessSettings", ctx, cfg)} | |||
| 997 | + | } | |||
| 998 | + | ||||
| 999 | + | func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call { | |||
| 1000 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 1001 | + | var arg0 context.Context | |||
| 1002 | + | if args[0] != nil { | |||
| 1003 | + | arg0 = args[0].(context.Context) | |||
| 1004 | + | } | |||
| 1005 | + | var arg1 *port.MemoryPressureConfig | |||
| 1006 | + | if args[1] != nil { | |||
| 1007 | + | arg1 = args[1].(*port.MemoryPressureConfig) | |||
| 1008 | + | } | |||
| 1009 | + | run( | |||
| 1010 | + | arg0, | |||
| 1011 | + | arg1, | |||
| 1012 | + | ) | |||
| 1013 | + | }) | |||
| 1014 | + | return _c | |||
| 1015 | + | } | |||
| 1016 | + | ||||
| 1017 | + | func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Return(err error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call { | |||
| 1018 | + | _c.Call.Return(err) | |||
| 1019 | + | return _c | |||
| 1020 | + | } | |||
| 1021 | + | ||||
| 1022 | + | func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call { | |||
| 1023 | + | _c.Call.Return(run) | |||
| 1024 | + | return _c | |||
| 1025 | + | } | |||
| 1026 | + | ||||
| 1027 | + | // ApplyWebProcessSettings provides a mock function for the type MockMemoryPressureApplier | |||
| 1028 | + | func (_mock *MockMemoryPressureApplier) ApplyWebProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error) { | |||
| 1029 | + | ret := _mock.Called(ctx, cfg) | |||
| 1030 | + | ||||
| 1031 | + | if len(ret) == 0 { | |||
| 1032 | + | panic("no return value specified for ApplyWebProcessSettings") | |||
| 1033 | + | } | |||
| 1034 | + | ||||
| 1035 | + | var r0 any | |||
| 1036 | + | var r1 error | |||
| 1037 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) (any, error)); ok { | |||
| 1038 | + | return returnFunc(ctx, cfg) | |||
| 1039 | + | } | |||
| 1040 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) any); ok { | |||
| 1041 | + | r0 = returnFunc(ctx, cfg) | |||
| 1042 | + | } else { | |||
| 1043 | + | if ret.Get(0) != nil { | |||
| 1044 | + | r0 = ret.Get(0).(any) | |||
| 1045 | + | } | |||
| 1046 | + | } | |||
| 1047 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, *port.MemoryPressureConfig) error); ok { | |||
| 1048 | + | r1 = returnFunc(ctx, cfg) | |||
| 1049 | + | } else { | |||
| 1050 | + | r1 = ret.Error(1) | |||
| 1051 | + | } | |||
| 1052 | + | return r0, r1 | |||
| 1053 | + | } | |||
| 1054 | + | ||||
| 1055 | + | // MockMemoryPressureApplier_ApplyWebProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyWebProcessSettings' | |||
| 1056 | + | type MockMemoryPressureApplier_ApplyWebProcessSettings_Call struct { | |||
| 1057 | + | *mock.Call | |||
| 1058 | + | } | |||
| 1059 | + | ||||
| 1060 | + | // ApplyWebProcessSettings is a helper method to define mock.On call | |||
| 1061 | + | // - ctx context.Context | |||
| 1062 | + | // - cfg *port.MemoryPressureConfig | |||
| 1063 | + | func (_e *MockMemoryPressureApplier_Expecter) ApplyWebProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call { | |||
| 1064 | + | return &MockMemoryPressureApplier_ApplyWebProcessSettings_Call{Call: _e.mock.On("ApplyWebProcessSettings", ctx, cfg)} | |||
| 1065 | + | } | |||
| 1066 | + | ||||
| 1067 | + | func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call { | |||
| 1068 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 1069 | + | var arg0 context.Context | |||
| 1070 | + | if args[0] != nil { | |||
| 1071 | + | arg0 = args[0].(context.Context) | |||
| 1072 | + | } | |||
| 1073 | + | var arg1 *port.MemoryPressureConfig | |||
| 1074 | + | if args[1] != nil { | |||
| 1075 | + | arg1 = args[1].(*port.MemoryPressureConfig) | |||
| 1076 | + | } | |||
| 1077 | + | run( | |||
| 1078 | + | arg0, | |||
| 1079 | + | arg1, | |||
| 1080 | + | ) | |||
| 1081 | + | }) | |||
| 1082 | + | return _c | |||
| 1083 | + | } | |||
| 1084 | + | ||||
| 1085 | + | func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Return(v any, err error) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call { | |||
| 1086 | + | _c.Call.Return(v, err) | |||
| 1087 | + | return _c | |||
| 1088 | + | } | |||
| 1089 | + | ||||
| 1090 | + | func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call { | |||
| 1091 | + | _c.Call.Return(run) | |||
| 1092 | + | return _c | |||
| 1093 | + | } | |||
| 1094 | + | ||||
| 1095 | + | // NewMockWebKitContextProvider creates a new instance of MockWebKitContextProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |||
| 1096 | + | // The first argument is typically a *testing.T value. | |||
| 1097 | + | func NewMockWebKitContextProvider(t interface { | |||
| 1098 | + | mock.TestingT | |||
| 1099 | + | Cleanup(func()) | |||
| 1100 | + | }) *MockWebKitContextProvider { | |||
| 1101 | + | mock := &MockWebKitContextProvider{} | |||
| 1102 | + | mock.Mock.Test(t) | |||
| 1103 | + | ||||
| 1104 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |||
| 1105 | + | ||||
| 1106 | + | return mock | |||
| 1107 | + | } | |||
| 1108 | + | ||||
| 1109 | + | // MockWebKitContextProvider is an autogenerated mock type for the WebKitContextProvider type | |||
| 1110 | + | type MockWebKitContextProvider struct { | |||
| 1111 | + | mock.Mock | |||
| 1112 | + | } | |||
| 1113 | + | ||||
| 1114 | + | type MockWebKitContextProvider_Expecter struct { | |||
| 1115 | + | mock *mock.Mock | |||
| 1116 | + | } | |||
| 1117 | + | ||||
| 1118 | + | func (_m *MockWebKitContextProvider) EXPECT() *MockWebKitContextProvider_Expecter { | |||
| 1119 | + | return &MockWebKitContextProvider_Expecter{mock: &_m.Mock} | |||
| 1120 | + | } | |||
| 1121 | + | ||||
| 1122 | + | // CacheDir provides a mock function for the type MockWebKitContextProvider | |||
| 1123 | + | func (_mock *MockWebKitContextProvider) CacheDir() string { | |||
| 1124 | + | ret := _mock.Called() | |||
| 1125 | + | ||||
| 1126 | + | if len(ret) == 0 { | |||
| 1127 | + | panic("no return value specified for CacheDir") | |||
| 1128 | + | } | |||
| 1129 | + | ||||
| 1130 | + | var r0 string | |||
| 1131 | + | if returnFunc, ok := ret.Get(0).(func() string); ok { | |||
| 1132 | + | r0 = returnFunc() | |||
| 1133 | + | } else { | |||
| 1134 | + | r0 = ret.Get(0).(string) | |||
| 1135 | + | } | |||
| 1136 | + | return r0 | |||
| 1137 | + | } | |||
| 1138 | + | ||||
| 1139 | + | // MockWebKitContextProvider_CacheDir_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CacheDir' | |||
| 1140 | + | type MockWebKitContextProvider_CacheDir_Call struct { | |||
| 1141 | + | *mock.Call | |||
| 1142 | + | } | |||
| 1143 | + | ||||
| 1144 | + | // CacheDir is a helper method to define mock.On call | |||
| 1145 | + | func (_e *MockWebKitContextProvider_Expecter) CacheDir() *MockWebKitContextProvider_CacheDir_Call { | |||
| 1146 | + | return &MockWebKitContextProvider_CacheDir_Call{Call: _e.mock.On("CacheDir")} | |||
| 1147 | + | } | |||
| 1148 | + | ||||
| 1149 | + | func (_c *MockWebKitContextProvider_CacheDir_Call) Run(run func()) *MockWebKitContextProvider_CacheDir_Call { | |||
| 1150 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 1151 | + | run() | |||
| 1152 | + | }) | |||
| 1153 | + | return _c | |||
| 1154 | + | } | |||
| 1155 | + | ||||
| 1156 | + | func (_c *MockWebKitContextProvider_CacheDir_Call) Return(s string) *MockWebKitContextProvider_CacheDir_Call { | |||
| 1157 | + | _c.Call.Return(s) | |||
| 1158 | + | return _c | |||
| 1159 | + | } | |||
| 1160 | + | ||||
| 1161 | + | func (_c *MockWebKitContextProvider_CacheDir_Call) RunAndReturn(run func() string) *MockWebKitContextProvider_CacheDir_Call { | |||
| 1162 | + | _c.Call.Return(run) | |||
| 1163 | + | return _c | |||
| 1164 | + | } | |||
| 1165 | + | ||||
| 1166 | + | // Close provides a mock function for the type MockWebKitContextProvider | |||
| 1167 | + | func (_mock *MockWebKitContextProvider) Close() error { | |||
| 1168 | + | ret := _mock.Called() | |||
| 1169 | + | ||||
| 1170 | + | if len(ret) == 0 { | |||
| 1171 | + | panic("no return value specified for Close") | |||
| 1172 | + | } | |||
| 1173 | + | ||||
| 1174 | + | var r0 error | |||
| 1175 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |||
| 1176 | + | r0 = returnFunc() | |||
| 1177 | + | } else { | |||
| 1178 | + | r0 = ret.Error(0) | |||
| 1179 | + | } | |||
| 1180 | + | return r0 | |||
| 1181 | + | } | |||
| 1182 | + | ||||
| 1183 | + | // MockWebKitContextProvider_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' | |||
| 1184 | + | type MockWebKitContextProvider_Close_Call struct { | |||
| 1185 | + | *mock.Call | |||
| 1186 | + | } | |||
| 1187 | + | ||||
| 1188 | + | // Close is a helper method to define mock.On call | |||
| 1189 | + | func (_e *MockWebKitContextProvider_Expecter) Close() *MockWebKitContextProvider_Close_Call { | |||
| 1190 | + | return &MockWebKitContextProvider_Close_Call{Call: _e.mock.On("Close")} | |||
| 1191 | + | } | |||
| 1192 | + | ||||
| 1193 | + | func (_c *MockWebKitContextProvider_Close_Call) Run(run func()) *MockWebKitContextProvider_Close_Call { | |||
| 1194 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 1195 | + | run() | |||
| 1196 | + | }) | |||
| 1197 | + | return _c | |||
| 1198 | + | } | |||
| 1199 | + | ||||
| 1200 | + | func (_c *MockWebKitContextProvider_Close_Call) Return(err error) *MockWebKitContextProvider_Close_Call { | |||
| 1201 | + | _c.Call.Return(err) | |||
| 1202 | + | return _c | |||
| 1203 | + | } | |||
| 1204 | + | ||||
| 1205 | + | func (_c *MockWebKitContextProvider_Close_Call) RunAndReturn(run func() error) *MockWebKitContextProvider_Close_Call { | |||
| 1206 | + | _c.Call.Return(run) | |||
| 1207 | + | return _c | |||
| 1208 | + | } | |||
| 1209 | + | ||||
| 1210 | + | // DataDir provides a mock function for the type MockWebKitContextProvider | |||
| 1211 | + | func (_mock *MockWebKitContextProvider) DataDir() string { | |||
| 1212 | + | ret := _mock.Called() | |||
| 1213 | + | ||||
| 1214 | + | if len(ret) == 0 { | |||
| 1215 | + | panic("no return value specified for DataDir") | |||
| 1216 | + | } | |||
| 1217 | + | ||||
| 1218 | + | var r0 string | |||
| 1219 | + | if returnFunc, ok := ret.Get(0).(func() string); ok { | |||
| 1220 | + | r0 = returnFunc() | |||
| 1221 | + | } else { | |||
| 1222 | + | r0 = ret.Get(0).(string) | |||
| 1223 | + | } | |||
| 1224 | + | return r0 | |||
| 1225 | + | } | |||
| 1226 | + | ||||
| 1227 | + | // MockWebKitContextProvider_DataDir_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DataDir' | |||
| 1228 | + | type MockWebKitContextProvider_DataDir_Call struct { | |||
| 1229 | + | *mock.Call | |||
| 1230 | + | } | |||
| 1231 | + | ||||
| 1232 | + | // DataDir is a helper method to define mock.On call | |||
| 1233 | + | func (_e *MockWebKitContextProvider_Expecter) DataDir() *MockWebKitContextProvider_DataDir_Call { | |||
| 1234 | + | return &MockWebKitContextProvider_DataDir_Call{Call: _e.mock.On("DataDir")} | |||
| 1235 | + | } | |||
| 1236 | + | ||||
| 1237 | + | func (_c *MockWebKitContextProvider_DataDir_Call) Run(run func()) *MockWebKitContextProvider_DataDir_Call { | |||
| 1238 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 1239 | + | run() | |||
| 1240 | + | }) | |||
| 1241 | + | return _c | |||
| 1242 | + | } | |||
| 1243 | + | ||||
| 1244 | + | func (_c *MockWebKitContextProvider_DataDir_Call) Return(s string) *MockWebKitContextProvider_DataDir_Call { | |||
| 1245 | + | _c.Call.Return(s) | |||
| 1246 | + | return _c | |||
| 1247 | + | } | |||
| 1248 | + | ||||
| 1249 | + | func (_c *MockWebKitContextProvider_DataDir_Call) RunAndReturn(run func() string) *MockWebKitContextProvider_DataDir_Call { | |||
| 1250 | + | _c.Call.Return(run) | |||
| 1251 | + | return _c | |||
| 1252 | + | } | |||
| 1253 | + | ||||
| 1254 | + | // Initialize provides a mock function for the type MockWebKitContextProvider | |||
| 1255 | + | func (_mock *MockWebKitContextProvider) Initialize(ctx context.Context, opts port.WebKitContextOptions) error { | |||
| 1256 | + | ret := _mock.Called(ctx, opts) | |||
| 1257 | + | ||||
| 1258 | + | if len(ret) == 0 { | |||
| 1259 | + | panic("no return value specified for Initialize") | |||
| 1260 | + | } | |||
| 1261 | + | ||||
| 1262 | + | var r0 error | |||
| 1263 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, port.WebKitContextOptions) error); ok { | |||
| 1264 | + | r0 = returnFunc(ctx, opts) | |||
| 1265 | + | } else { | |||
| 1266 | + | r0 = ret.Error(0) | |||
| 1267 | + | } | |||
| 1268 | + | return r0 | |||
| 1269 | + | } | |||
| 1270 | + | ||||
| 1271 | + | // MockWebKitContextProvider_Initialize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Initialize' | |||
| 1272 | + | type MockWebKitContextProvider_Initialize_Call struct { | |||
| 1273 | + | *mock.Call | |||
| 1274 | + | } | |||
| 1275 | + | ||||
| 1276 | + | // Initialize is a helper method to define mock.On call | |||
| 1277 | + | // - ctx context.Context | |||
| 1278 | + | // - opts port.WebKitContextOptions | |||
| 1279 | + | func (_e *MockWebKitContextProvider_Expecter) Initialize(ctx interface{}, opts interface{}) *MockWebKitContextProvider_Initialize_Call { | |||
| 1280 | + | return &MockWebKitContextProvider_Initialize_Call{Call: _e.mock.On("Initialize", ctx, opts)} | |||
| 1281 | + | } | |||
| 1282 | + | ||||
| 1283 | + | func (_c *MockWebKitContextProvider_Initialize_Call) Run(run func(ctx context.Context, opts port.WebKitContextOptions)) *MockWebKitContextProvider_Initialize_Call { | |||
| 1284 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 1285 | + | var arg0 context.Context | |||
| 1286 | + | if args[0] != nil { | |||
| 1287 | + | arg0 = args[0].(context.Context) | |||
| 1288 | + | } | |||
| 1289 | + | var arg1 port.WebKitContextOptions | |||
| 1290 | + | if args[1] != nil { | |||
| 1291 | + | arg1 = args[1].(port.WebKitContextOptions) | |||
| 1292 | + | } | |||
| 1293 | + | run( | |||
| 1294 | + | arg0, | |||
| 1295 | + | arg1, | |||
| 1296 | + | ) | |||
| 1297 | + | }) | |||
| 1298 | + | return _c | |||
| 1299 | + | } | |||
| 1300 | + | ||||
| 1301 | + | func (_c *MockWebKitContextProvider_Initialize_Call) Return(err error) *MockWebKitContextProvider_Initialize_Call { | |||
| 1302 | + | _c.Call.Return(err) | |||
| 1303 | + | return _c | |||
| 1304 | + | } | |||
| 1305 | + | ||||
| 1306 | + | func (_c *MockWebKitContextProvider_Initialize_Call) RunAndReturn(run func(ctx context.Context, opts port.WebKitContextOptions) error) *MockWebKitContextProvider_Initialize_Call { | |||
| 1307 | + | _c.Call.Return(run) | |||
| 1308 | + | return _c | |||
| 1309 | + | } | |||
| 1310 | + | ||||
| 1311 | + | // PrefetchDNS provides a mock function for the type MockWebKitContextProvider | |||
| 1312 | + | func (_mock *MockWebKitContextProvider) PrefetchDNS(hostname string) { | |||
| 1313 | + | _mock.Called(hostname) | |||
| 1314 | + | return | |||
| 1315 | + | } | |||
| 1316 | + | ||||
| 1317 | + | // MockWebKitContextProvider_PrefetchDNS_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrefetchDNS' | |||
| 1318 | + | type MockWebKitContextProvider_PrefetchDNS_Call struct { | |||
| 1319 | + | *mock.Call | |||
| 1320 | + | } | |||
| 1321 | + | ||||
| 1322 | + | // PrefetchDNS is a helper method to define mock.On call | |||
| 1323 | + | // - hostname string | |||
| 1324 | + | func (_e *MockWebKitContextProvider_Expecter) PrefetchDNS(hostname interface{}) *MockWebKitContextProvider_PrefetchDNS_Call { | |||
| 1325 | + | return &MockWebKitContextProvider_PrefetchDNS_Call{Call: _e.mock.On("PrefetchDNS", hostname)} | |||
| 1326 | + | } | |||
| 1327 | + | ||||
| 1328 | + | func (_c *MockWebKitContextProvider_PrefetchDNS_Call) Run(run func(hostname string)) *MockWebKitContextProvider_PrefetchDNS_Call { | |||
| 1329 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 1330 | + | var arg0 string | |||
| 1331 | + | if args[0] != nil { | |||
| 1332 | + | arg0 = args[0].(string) | |||
| 1333 | + | } | |||
| 1334 | + | run( | |||
| 1335 | + | arg0, | |||
| 1336 | + | ) | |||
| 1337 | + | }) | |||
| 1338 | + | return _c | |||
| 1339 | + | } | |||
| 1340 | + | ||||
| 1341 | + | func (_c *MockWebKitContextProvider_PrefetchDNS_Call) Return() *MockWebKitContextProvider_PrefetchDNS_Call { | |||
| 1342 | + | _c.Call.Return() | |||
| 1343 | + | return _c | |||
| 1344 | + | } | |||
| 1345 | + | ||||
| 1346 | + | func (_c *MockWebKitContextProvider_PrefetchDNS_Call) RunAndReturn(run func(hostname string)) *MockWebKitContextProvider_PrefetchDNS_Call { | |||
| 1347 | + | _c.Run(run) | |||
| 1348 | + | return _c | |||
| 1349 | + | } | |||
| 1350 | + | ||||
| 943 | // NewMockWebView creates a new instance of MockWebView. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | 1351 | // NewMockWebView creates a new instance of MockWebView. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | ||
| 944 | // The first argument is typically a *testing.T value. | 1352 | // The first argument is typically a *testing.T value. | ||
| 945 | func NewMockWebView(t interface { | 1353 | func NewMockWebView(t interface { | ||
M internal/application/port/rendering_env.go +10 -0
| @@ -46,6 +46,16 @@ type RenderingEnvSettings struct { | |||
| 46 | 46 | ShowFPS bool | |
| 47 | 47 | SampleMemory bool | |
| 48 | 48 | DebugFrames bool | |
| 49 | + | ||
| 50 | + | // --- Skia rendering thread settings (from PerformanceConfig) --- | |
| 51 | + | // SkiaCPUPaintingThreads sets WEBKIT_SKIA_CPU_PAINTING_THREADS. | |
| 52 | + | // 0 means unset (use WebKit default). | |
| 53 | + | SkiaCPUPaintingThreads int | |
| 54 | + | // SkiaGPUPaintingThreads sets WEBKIT_SKIA_GPU_PAINTING_THREADS. | |
| 55 | + | // -1 means unset; 0 disables GPU tile painting. | |
| 56 | + | SkiaGPUPaintingThreads int | |
| 57 | + | // SkiaEnableCPURendering forces CPU rendering via WEBKIT_SKIA_ENABLE_CPU_RENDERING=1. | |
| 58 | + | SkiaEnableCPURendering bool | |
| 49 | 59 | } | |
| 50 | 60 | ||
| 51 | 61 | // RenderingEnvManager configures rendering environment variables | |
| @@ -46,6 +46,16 @@ type RenderingEnvSettings struct { | |||||
| 46 | ShowFPS bool | 46 | ShowFPS bool | ||
| 47 | SampleMemory bool | 47 | SampleMemory bool | ||
| 48 | DebugFrames bool | 48 | DebugFrames bool | ||
| 49 | + | ||||
| 50 | + | // --- Skia rendering thread settings (from PerformanceConfig) --- | |||
| 51 | + | // SkiaCPUPaintingThreads sets WEBKIT_SKIA_CPU_PAINTING_THREADS. | |||
| 52 | + | // 0 means unset (use WebKit default). | |||
| 53 | + | SkiaCPUPaintingThreads int | |||
| 54 | + | // SkiaGPUPaintingThreads sets WEBKIT_SKIA_GPU_PAINTING_THREADS. | |||
| 55 | + | // -1 means unset; 0 disables GPU tile painting. | |||
| 56 | + | SkiaGPUPaintingThreads int | |||
| 57 | + | // SkiaEnableCPURendering forces CPU rendering via WEBKIT_SKIA_ENABLE_CPU_RENDERING=1. | |||
| 58 | + | SkiaEnableCPURendering bool | |||
| 49 | } | 59 | } | ||
| 50 | 60 | ||||
| 51 | // RenderingEnvManager configures rendering environment variables | 61 | // RenderingEnvManager configures rendering environment variables | ||
A internal/application/port/webkit_context.go +101 -0
| @@ -0,0 +1,101 @@ | |||
| 1 | + | // Package port defines interfaces for external dependencies. | |
| 2 | + | package port | |
| 3 | + | ||
| 4 | + | import "context" | |
| 5 | + | ||
| 6 | + | // MemoryPressureConfig holds memory pressure settings for a WebKit process. | |
| 7 | + | // Zero values mean "use WebKit defaults" except for KillThreshold where -1 means unset. | |
| 8 | + | type MemoryPressureConfig struct { | |
| 9 | + | // MemoryLimitMB sets the memory limit in megabytes. | |
| 10 | + | // 0 means unset (uses WebKit default: system RAM capped at 3GB). | |
| 11 | + | MemoryLimitMB int | |
| 12 | + | ||
| 13 | + | // PollIntervalSec sets the interval for memory usage checks. | |
| 14 | + | // 0 means unset (uses WebKit default: 30 seconds). | |
| 15 | + | PollIntervalSec float64 | |
| 16 | + | ||
| 17 | + | // ConservativeThreshold sets threshold for conservative memory release. | |
| 18 | + | // Must be in (0, 1). 0 means unset (uses WebKit default: 0.33). | |
| 19 | + | ConservativeThreshold float64 | |
| 20 | + | ||
| 21 | + | // StrictThreshold sets threshold for strict memory release. | |
| 22 | + | // Must be in (0, 1). 0 means unset (uses WebKit default: 0.5). | |
| 23 | + | StrictThreshold float64 | |
| 24 | + | ||
| 25 | + | // KillThreshold sets threshold for killing the process. | |
| 26 | + | // -1 means unset; 0 means never kill; values > 1 are allowed by WebKit. | |
| 27 | + | KillThreshold float64 | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | // IsConfigured returns true if any memory pressure setting is configured. | |
| 31 | + | func (c *MemoryPressureConfig) IsConfigured() bool { | |
| 32 | + | if c == nil { | |
| 33 | + | return false | |
| 34 | + | } | |
| 35 | + | return c.MemoryLimitMB > 0 || | |
| 36 | + | c.PollIntervalSec > 0 || | |
| 37 | + | c.ConservativeThreshold > 0 || | |
| 38 | + | c.StrictThreshold > 0 || | |
| 39 | + | c.KillThreshold >= 0 | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | // WebKitContextOptions configures WebKitContext creation. | |
| 43 | + | type WebKitContextOptions struct { | |
| 44 | + | // DataDir is the directory for persistent data (cookies, storage, etc.). | |
| 45 | + | DataDir string | |
| 46 | + | ||
| 47 | + | // CacheDir is the directory for cache data. | |
| 48 | + | CacheDir string | |
| 49 | + | ||
| 50 | + | // WebProcessMemory configures memory pressure for web processes. | |
| 51 | + | // nil means use WebKit defaults. | |
| 52 | + | WebProcessMemory *MemoryPressureConfig | |
| 53 | + | ||
| 54 | + | // NetworkProcessMemory configures memory pressure for the network process. | |
| 55 | + | // nil means use WebKit defaults. | |
| 56 | + | NetworkProcessMemory *MemoryPressureConfig | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | // IsWebProcessMemoryConfigured returns true if web process memory settings are configured. | |
| 60 | + | func (o *WebKitContextOptions) IsWebProcessMemoryConfigured() bool { | |
| 61 | + | return o.WebProcessMemory.IsConfigured() | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | // IsNetworkProcessMemoryConfigured returns true if network process memory settings are configured. | |
| 65 | + | func (o *WebKitContextOptions) IsNetworkProcessMemoryConfigured() bool { | |
| 66 | + | return o.NetworkProcessMemory.IsConfigured() | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | // MemoryPressureApplier applies memory pressure settings to WebKit processes. | |
| 70 | + | // This interface abstracts the WebKit-specific memory pressure configuration | |
| 71 | + | // to allow testing without WebKit dependencies. | |
| 72 | + | type MemoryPressureApplier interface { | |
| 73 | + | // ApplyNetworkProcessSettings applies memory pressure settings to the network process. | |
| 74 | + | // Must be called BEFORE creating any NetworkSession. | |
| 75 | + | ApplyNetworkProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) error | |
| 76 | + | ||
| 77 | + | // ApplyWebProcessSettings applies memory pressure settings to web processes. | |
| 78 | + | // Returns an opaque settings object that should be passed to WebContext creation. | |
| 79 | + | // Returns nil if no settings are configured. | |
| 80 | + | ApplyWebProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) (any, error) | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | // WebKitContextProvider creates and manages WebKit contexts. | |
| 84 | + | // This interface abstracts WebKit context creation for testability. | |
| 85 | + | type WebKitContextProvider interface { | |
| 86 | + | // Initialize sets up the WebKit context with the given options. | |
| 87 | + | // Must be called before any other methods. | |
| 88 | + | Initialize(ctx context.Context, opts WebKitContextOptions) error | |
| 89 | + | ||
| 90 | + | // DataDir returns the data directory path. | |
| 91 | + | DataDir() string | |
| 92 | + | ||
| 93 | + | // CacheDir returns the cache directory path. | |
| 94 | + | CacheDir() string | |
| 95 | + | ||
| 96 | + | // PrefetchDNS prefetches DNS for the given hostname. | |
| 97 | + | PrefetchDNS(hostname string) | |
| 98 | + | ||
| 99 | + | // Close releases resources held by the context. | |
| 100 | + | Close() error | |
| 101 | + | } | |
| @@ -0,0 +1,101 @@ | |||||
| 1 | + | // Package port defines interfaces for external dependencies. | |||
| 2 | + | package port | |||
| 3 | + | ||||
| 4 | + | import "context" | |||
| 5 | + | ||||
| 6 | + | // MemoryPressureConfig holds memory pressure settings for a WebKit process. | |||
| 7 | + | // Zero values mean "use WebKit defaults" except for KillThreshold where -1 means unset. | |||
| 8 | + | type MemoryPressureConfig struct { | |||
| 9 | + | // MemoryLimitMB sets the memory limit in megabytes. | |||
| 10 | + | // 0 means unset (uses WebKit default: system RAM capped at 3GB). | |||
| 11 | + | MemoryLimitMB int | |||
| 12 | + | ||||
| 13 | + | // PollIntervalSec sets the interval for memory usage checks. | |||
| 14 | + | // 0 means unset (uses WebKit default: 30 seconds). | |||
| 15 | + | PollIntervalSec float64 | |||
| 16 | + | ||||
| 17 | + | // ConservativeThreshold sets threshold for conservative memory release. | |||
| 18 | + | // Must be in (0, 1). 0 means unset (uses WebKit default: 0.33). | |||
| 19 | + | ConservativeThreshold float64 | |||
| 20 | + | ||||
| 21 | + | // StrictThreshold sets threshold for strict memory release. | |||
| 22 | + | // Must be in (0, 1). 0 means unset (uses WebKit default: 0.5). | |||
| 23 | + | StrictThreshold float64 | |||
| 24 | + | ||||
| 25 | + | // KillThreshold sets threshold for killing the process. | |||
| 26 | + | // -1 means unset; 0 means never kill; values > 1 are allowed by WebKit. | |||
| 27 | + | KillThreshold float64 | |||
| 28 | + | } | |||
| 29 | + | ||||
| 30 | + | // IsConfigured returns true if any memory pressure setting is configured. | |||
| 31 | + | func (c *MemoryPressureConfig) IsConfigured() bool { | |||
| 32 | + | if c == nil { | |||
| 33 | + | return false | |||
| 34 | + | } | |||
| 35 | + | return c.MemoryLimitMB > 0 || | |||
| 36 | + | c.PollIntervalSec > 0 || | |||
| 37 | + | c.ConservativeThreshold > 0 || | |||
| 38 | + | c.StrictThreshold > 0 || | |||
| 39 | + | c.KillThreshold >= 0 | |||
| 40 | + | } | |||
| 41 | + | ||||
| 42 | + | // WebKitContextOptions configures WebKitContext creation. | |||
| 43 | + | type WebKitContextOptions struct { | |||
| 44 | + | // DataDir is the directory for persistent data (cookies, storage, etc.). | |||
| 45 | + | DataDir string | |||
| 46 | + | ||||
| 47 | + | // CacheDir is the directory for cache data. | |||
| 48 | + | CacheDir string | |||
| 49 | + | ||||
| 50 | + | // WebProcessMemory configures memory pressure for web processes. | |||
| 51 | + | // nil means use WebKit defaults. | |||
| 52 | + | WebProcessMemory *MemoryPressureConfig | |||
| 53 | + | ||||
| 54 | + | // NetworkProcessMemory configures memory pressure for the network process. | |||
| 55 | + | // nil means use WebKit defaults. | |||
| 56 | + | NetworkProcessMemory *MemoryPressureConfig | |||
| 57 | + | } | |||
| 58 | + | ||||
| 59 | + | // IsWebProcessMemoryConfigured returns true if web process memory settings are configured. | |||
| 60 | + | func (o *WebKitContextOptions) IsWebProcessMemoryConfigured() bool { | |||
| 61 | + | return o.WebProcessMemory.IsConfigured() | |||
| 62 | + | } | |||
| 63 | + | ||||
| 64 | + | // IsNetworkProcessMemoryConfigured returns true if network process memory settings are configured. | |||
| 65 | + | func (o *WebKitContextOptions) IsNetworkProcessMemoryConfigured() bool { | |||
| 66 | + | return o.NetworkProcessMemory.IsConfigured() | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | // MemoryPressureApplier applies memory pressure settings to WebKit processes. | |||
| 70 | + | // This interface abstracts the WebKit-specific memory pressure configuration | |||
| 71 | + | // to allow testing without WebKit dependencies. | |||
| 72 | + | type MemoryPressureApplier interface { | |||
| 73 | + | // ApplyNetworkProcessSettings applies memory pressure settings to the network process. | |||
| 74 | + | // Must be called BEFORE creating any NetworkSession. | |||
| 75 | + | ApplyNetworkProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) error | |||
| 76 | + | ||||
| 77 | + | // ApplyWebProcessSettings applies memory pressure settings to web processes. | |||
| 78 | + | // Returns an opaque settings object that should be passed to WebContext creation. | |||
| 79 | + | // Returns nil if no settings are configured. | |||
| 80 | + | ApplyWebProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) (any, error) | |||
| 81 | + | } | |||
| 82 | + | ||||
| 83 | + | // WebKitContextProvider creates and manages WebKit contexts. | |||
| 84 | + | // This interface abstracts WebKit context creation for testability. | |||
| 85 | + | type WebKitContextProvider interface { | |||
| 86 | + | // Initialize sets up the WebKit context with the given options. | |||
| 87 | + | // Must be called before any other methods. | |||
| 88 | + | Initialize(ctx context.Context, opts WebKitContextOptions) error | |||
| 89 | + | ||||
| 90 | + | // DataDir returns the data directory path. | |||
| 91 | + | DataDir() string | |||
| 92 | + | ||||
| 93 | + | // CacheDir returns the cache directory path. | |||
| 94 | + | CacheDir() string | |||
| 95 | + | ||||
| 96 | + | // PrefetchDNS prefetches DNS for the given hostname. | |||
| 97 | + | PrefetchDNS(hostname string) | |||
| 98 | + | ||||
| 99 | + | // Close releases resources held by the context. | |||
| 100 | + | Close() error | |||
| 101 | + | } | |||
A internal/application/port/webkit_context_test.go +185 -0
| @@ -0,0 +1,185 @@ | |||
| 1 | + | package port_test | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "testing" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/dumber/internal/application/port" | |
| 7 | + | "github.com/stretchr/testify/assert" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | func TestMemoryPressureConfig_IsConfigured(t *testing.T) { | |
| 11 | + | tests := []struct { | |
| 12 | + | name string | |
| 13 | + | config *port.MemoryPressureConfig | |
| 14 | + | expected bool | |
| 15 | + | }{ | |
| 16 | + | { | |
| 17 | + | name: "nil config", | |
| 18 | + | config: nil, | |
| 19 | + | expected: false, | |
| 20 | + | }, | |
| 21 | + | { | |
| 22 | + | name: "empty config with default kill threshold", | |
| 23 | + | config: &port.MemoryPressureConfig{ | |
| 24 | + | KillThreshold: -1, // explicit unset | |
| 25 | + | }, | |
| 26 | + | expected: false, | |
| 27 | + | }, | |
| 28 | + | { | |
| 29 | + | name: "only MemoryLimitMB set", | |
| 30 | + | config: &port.MemoryPressureConfig{ | |
| 31 | + | MemoryLimitMB: 1024, | |
| 32 | + | }, | |
| 33 | + | expected: true, | |
| 34 | + | }, | |
| 35 | + | { | |
| 36 | + | name: "only PollIntervalSec set", | |
| 37 | + | config: &port.MemoryPressureConfig{ | |
| 38 | + | PollIntervalSec: 30.0, | |
| 39 | + | }, | |
| 40 | + | expected: true, | |
| 41 | + | }, | |
| 42 | + | { | |
| 43 | + | name: "only ConservativeThreshold set", | |
| 44 | + | config: &port.MemoryPressureConfig{ | |
| 45 | + | ConservativeThreshold: 0.33, | |
| 46 | + | }, | |
| 47 | + | expected: true, | |
| 48 | + | }, | |
| 49 | + | { | |
| 50 | + | name: "only StrictThreshold set", | |
| 51 | + | config: &port.MemoryPressureConfig{ | |
| 52 | + | StrictThreshold: 0.5, | |
| 53 | + | }, | |
| 54 | + | expected: true, | |
| 55 | + | }, | |
| 56 | + | { | |
| 57 | + | name: "KillThreshold set to 0 (never kill)", | |
| 58 | + | config: &port.MemoryPressureConfig{ | |
| 59 | + | KillThreshold: 0, | |
| 60 | + | }, | |
| 61 | + | expected: true, | |
| 62 | + | }, | |
| 63 | + | { | |
| 64 | + | name: "KillThreshold set to positive value", | |
| 65 | + | config: &port.MemoryPressureConfig{ | |
| 66 | + | KillThreshold: 1.5, | |
| 67 | + | }, | |
| 68 | + | expected: true, | |
| 69 | + | }, | |
| 70 | + | { | |
| 71 | + | name: "KillThreshold unset (-1)", | |
| 72 | + | config: &port.MemoryPressureConfig{ | |
| 73 | + | KillThreshold: -1, | |
| 74 | + | }, | |
| 75 | + | expected: false, | |
| 76 | + | }, | |
| 77 | + | { | |
| 78 | + | name: "all defaults (unset)", | |
| 79 | + | config: &port.MemoryPressureConfig{ | |
| 80 | + | MemoryLimitMB: 0, | |
| 81 | + | PollIntervalSec: 0, | |
| 82 | + | ConservativeThreshold: 0, | |
| 83 | + | StrictThreshold: 0, | |
| 84 | + | KillThreshold: -1, | |
| 85 | + | }, | |
| 86 | + | expected: false, | |
| 87 | + | }, | |
| 88 | + | { | |
| 89 | + | name: "fully configured", | |
| 90 | + | config: &port.MemoryPressureConfig{ | |
| 91 | + | MemoryLimitMB: 2048, | |
| 92 | + | PollIntervalSec: 15.0, | |
| 93 | + | ConservativeThreshold: 0.25, | |
| 94 | + | StrictThreshold: 0.6, | |
| 95 | + | KillThreshold: 0.9, | |
| 96 | + | }, | |
| 97 | + | expected: true, | |
| 98 | + | }, | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | for _, tt := range tests { | |
| 102 | + | t.Run(tt.name, func(t *testing.T) { | |
| 103 | + | result := tt.config.IsConfigured() | |
| 104 | + | assert.Equal(t, tt.expected, result) | |
| 105 | + | }) | |
| 106 | + | } | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | func TestWebKitContextOptions_IsWebProcessMemoryConfigured(t *testing.T) { | |
| 110 | + | tests := []struct { | |
| 111 | + | name string | |
| 112 | + | opts port.WebKitContextOptions | |
| 113 | + | expected bool | |
| 114 | + | }{ | |
| 115 | + | { | |
| 116 | + | name: "nil WebProcessMemory", | |
| 117 | + | opts: port.WebKitContextOptions{}, | |
| 118 | + | expected: false, | |
| 119 | + | }, | |
| 120 | + | { | |
| 121 | + | name: "empty WebProcessMemory with default kill threshold", | |
| 122 | + | opts: port.WebKitContextOptions{ | |
| 123 | + | WebProcessMemory: &port.MemoryPressureConfig{ | |
| 124 | + | KillThreshold: -1, // explicit unset | |
| 125 | + | }, | |
| 126 | + | }, | |
| 127 | + | expected: false, | |
| 128 | + | }, | |
| 129 | + | { | |
| 130 | + | name: "configured WebProcessMemory", | |
| 131 | + | opts: port.WebKitContextOptions{ | |
| 132 | + | WebProcessMemory: &port.MemoryPressureConfig{ | |
| 133 | + | MemoryLimitMB: 1024, | |
| 134 | + | }, | |
| 135 | + | }, | |
| 136 | + | expected: true, | |
| 137 | + | }, | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | for _, tt := range tests { | |
| 141 | + | t.Run(tt.name, func(t *testing.T) { | |
| 142 | + | result := tt.opts.IsWebProcessMemoryConfigured() | |
| 143 | + | assert.Equal(t, tt.expected, result) | |
| 144 | + | }) | |
| 145 | + | } | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | func TestWebKitContextOptions_IsNetworkProcessMemoryConfigured(t *testing.T) { | |
| 149 | + | tests := []struct { | |
| 150 | + | name string | |
| 151 | + | opts port.WebKitContextOptions | |
| 152 | + | expected bool | |
| 153 | + | }{ | |
| 154 | + | { | |
| 155 | + | name: "nil NetworkProcessMemory", | |
| 156 | + | opts: port.WebKitContextOptions{}, | |
| 157 | + | expected: false, | |
| 158 | + | }, | |
| 159 | + | { | |
| 160 | + | name: "empty NetworkProcessMemory with default kill threshold", | |
| 161 | + | opts: port.WebKitContextOptions{ | |
| 162 | + | NetworkProcessMemory: &port.MemoryPressureConfig{ | |
| 163 | + | KillThreshold: -1, // explicit unset | |
| 164 | + | }, | |
| 165 | + | }, | |
| 166 | + | expected: false, | |
| 167 | + | }, | |
| 168 | + | { | |
| 169 | + | name: "configured NetworkProcessMemory", | |
| 170 | + | opts: port.WebKitContextOptions{ | |
| 171 | + | NetworkProcessMemory: &port.MemoryPressureConfig{ | |
| 172 | + | PollIntervalSec: 20.0, | |
| 173 | + | }, | |
| 174 | + | }, | |
| 175 | + | expected: true, | |
| 176 | + | }, | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | for _, tt := range tests { | |
| 180 | + | t.Run(tt.name, func(t *testing.T) { | |
| 181 | + | result := tt.opts.IsNetworkProcessMemoryConfigured() | |
| 182 | + | assert.Equal(t, tt.expected, result) | |
| 183 | + | }) | |
| 184 | + | } | |
| 185 | + | } | |
| @@ -0,0 +1,185 @@ | |||||
| 1 | + | package port_test | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "testing" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 7 | + | "github.com/stretchr/testify/assert" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | func TestMemoryPressureConfig_IsConfigured(t *testing.T) { | |||
| 11 | + | tests := []struct { | |||
| 12 | + | name string | |||
| 13 | + | config *port.MemoryPressureConfig | |||
| 14 | + | expected bool | |||
| 15 | + | }{ | |||
| 16 | + | { | |||
| 17 | + | name: "nil config", | |||
| 18 | + | config: nil, | |||
| 19 | + | expected: false, | |||
| 20 | + | }, | |||
| 21 | + | { | |||
| 22 | + | name: "empty config with default kill threshold", | |||
| 23 | + | config: &port.MemoryPressureConfig{ | |||
| 24 | + | KillThreshold: -1, // explicit unset | |||
| 25 | + | }, | |||
| 26 | + | expected: false, | |||
| 27 | + | }, | |||
| 28 | + | { | |||
| 29 | + | name: "only MemoryLimitMB set", | |||
| 30 | + | config: &port.MemoryPressureConfig{ | |||
| 31 | + | MemoryLimitMB: 1024, | |||
| 32 | + | }, | |||
| 33 | + | expected: true, | |||
| 34 | + | }, | |||
| 35 | + | { | |||
| 36 | + | name: "only PollIntervalSec set", | |||
| 37 | + | config: &port.MemoryPressureConfig{ | |||
| 38 | + | PollIntervalSec: 30.0, | |||
| 39 | + | }, | |||
| 40 | + | expected: true, | |||
| 41 | + | }, | |||
| 42 | + | { | |||
| 43 | + | name: "only ConservativeThreshold set", | |||
| 44 | + | config: &port.MemoryPressureConfig{ | |||
| 45 | + | ConservativeThreshold: 0.33, | |||
| 46 | + | }, | |||
| 47 | + | expected: true, | |||
| 48 | + | }, | |||
| 49 | + | { | |||
| 50 | + | name: "only StrictThreshold set", | |||
| 51 | + | config: &port.MemoryPressureConfig{ | |||
| 52 | + | StrictThreshold: 0.5, | |||
| 53 | + | }, | |||
| 54 | + | expected: true, | |||
| 55 | + | }, | |||
| 56 | + | { | |||
| 57 | + | name: "KillThreshold set to 0 (never kill)", | |||
| 58 | + | config: &port.MemoryPressureConfig{ | |||
| 59 | + | KillThreshold: 0, | |||
| 60 | + | }, | |||
| 61 | + | expected: true, | |||
| 62 | + | }, | |||
| 63 | + | { | |||
| 64 | + | name: "KillThreshold set to positive value", | |||
| 65 | + | config: &port.MemoryPressureConfig{ | |||
| 66 | + | KillThreshold: 1.5, | |||
| 67 | + | }, | |||
| 68 | + | expected: true, | |||
| 69 | + | }, | |||
| 70 | + | { | |||
| 71 | + | name: "KillThreshold unset (-1)", | |||
| 72 | + | config: &port.MemoryPressureConfig{ | |||
| 73 | + | KillThreshold: -1, | |||
| 74 | + | }, | |||
| 75 | + | expected: false, | |||
| 76 | + | }, | |||
| 77 | + | { | |||
| 78 | + | name: "all defaults (unset)", | |||
| 79 | + | config: &port.MemoryPressureConfig{ | |||
| 80 | + | MemoryLimitMB: 0, | |||
| 81 | + | PollIntervalSec: 0, | |||
| 82 | + | ConservativeThreshold: 0, | |||
| 83 | + | StrictThreshold: 0, | |||
| 84 | + | KillThreshold: -1, | |||
| 85 | + | }, | |||
| 86 | + | expected: false, | |||
| 87 | + | }, | |||
| 88 | + | { | |||
| 89 | + | name: "fully configured", | |||
| 90 | + | config: &port.MemoryPressureConfig{ | |||
| 91 | + | MemoryLimitMB: 2048, | |||
| 92 | + | PollIntervalSec: 15.0, | |||
| 93 | + | ConservativeThreshold: 0.25, | |||
| 94 | + | StrictThreshold: 0.6, | |||
| 95 | + | KillThreshold: 0.9, | |||
| 96 | + | }, | |||
| 97 | + | expected: true, | |||
| 98 | + | }, | |||
| 99 | + | } | |||
| 100 | + | ||||
| 101 | + | for _, tt := range tests { | |||
| 102 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 103 | + | result := tt.config.IsConfigured() | |||
| 104 | + | assert.Equal(t, tt.expected, result) | |||
| 105 | + | }) | |||
| 106 | + | } | |||
| 107 | + | } | |||
| 108 | + | ||||
| 109 | + | func TestWebKitContextOptions_IsWebProcessMemoryConfigured(t *testing.T) { | |||
| 110 | + | tests := []struct { | |||
| 111 | + | name string | |||
| 112 | + | opts port.WebKitContextOptions | |||
| 113 | + | expected bool | |||
| 114 | + | }{ | |||
| 115 | + | { | |||
| 116 | + | name: "nil WebProcessMemory", | |||
| 117 | + | opts: port.WebKitContextOptions{}, | |||
| 118 | + | expected: false, | |||
| 119 | + | }, | |||
| 120 | + | { | |||
| 121 | + | name: "empty WebProcessMemory with default kill threshold", | |||
| 122 | + | opts: port.WebKitContextOptions{ | |||
| 123 | + | WebProcessMemory: &port.MemoryPressureConfig{ | |||
| 124 | + | KillThreshold: -1, // explicit unset | |||
| 125 | + | }, | |||
| 126 | + | }, | |||
| 127 | + | expected: false, | |||
| 128 | + | }, | |||
| 129 | + | { | |||
| 130 | + | name: "configured WebProcessMemory", | |||
| 131 | + | opts: port.WebKitContextOptions{ | |||
| 132 | + | WebProcessMemory: &port.MemoryPressureConfig{ | |||
| 133 | + | MemoryLimitMB: 1024, | |||
| 134 | + | }, | |||
| 135 | + | }, | |||
| 136 | + | expected: true, | |||
| 137 | + | }, | |||
| 138 | + | } | |||
| 139 | + | ||||
| 140 | + | for _, tt := range tests { | |||
| 141 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 142 | + | result := tt.opts.IsWebProcessMemoryConfigured() | |||
| 143 | + | assert.Equal(t, tt.expected, result) | |||
| 144 | + | }) | |||
| 145 | + | } | |||
| 146 | + | } | |||
| 147 | + | ||||
| 148 | + | func TestWebKitContextOptions_IsNetworkProcessMemoryConfigured(t *testing.T) { | |||
| 149 | + | tests := []struct { | |||
| 150 | + | name string | |||
| 151 | + | opts port.WebKitContextOptions | |||
| 152 | + | expected bool | |||
| 153 | + | }{ | |||
| 154 | + | { | |||
| 155 | + | name: "nil NetworkProcessMemory", | |||
| 156 | + | opts: port.WebKitContextOptions{}, | |||
| 157 | + | expected: false, | |||
| 158 | + | }, | |||
| 159 | + | { | |||
| 160 | + | name: "empty NetworkProcessMemory with default kill threshold", | |||
| 161 | + | opts: port.WebKitContextOptions{ | |||
| 162 | + | NetworkProcessMemory: &port.MemoryPressureConfig{ | |||
| 163 | + | KillThreshold: -1, // explicit unset | |||
| 164 | + | }, | |||
| 165 | + | }, | |||
| 166 | + | expected: false, | |||
| 167 | + | }, | |||
| 168 | + | { | |||
| 169 | + | name: "configured NetworkProcessMemory", | |||
| 170 | + | opts: port.WebKitContextOptions{ | |||
| 171 | + | NetworkProcessMemory: &port.MemoryPressureConfig{ | |||
| 172 | + | PollIntervalSec: 20.0, | |||
| 173 | + | }, | |||
| 174 | + | }, | |||
| 175 | + | expected: true, | |||
| 176 | + | }, | |||
| 177 | + | } | |||
| 178 | + | ||||
| 179 | + | for _, tt := range tests { | |||
| 180 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 181 | + | result := tt.opts.IsNetworkProcessMemoryConfigured() | |||
| 182 | + | assert.Equal(t, tt.expected, result) | |||
| 183 | + | }) | |||
| 184 | + | } | |||
| 185 | + | } | |||
M internal/infrastructure/env/manager.go +18 -0
| @@ -54,6 +54,7 @@ func (m *Manager) ApplyEnvironment(ctx context.Context, settings port.RenderingE | |||
| 54 | 54 | m.applyWebKitEnv(settings) | |
| 55 | 55 | m.applyGTKEnv(settings) | |
| 56 | 56 | m.applyDebugEnv(settings) | |
| 57 | + | m.applySkiaEnv(settings) | |
| 57 | 58 | ||
| 58 | 59 | log.Debug(). | |
| 59 | 60 | Interface("vars", m.appliedVars). | |
| @@ -162,6 +163,23 @@ func (m *Manager) applyDebugEnv(settings port.RenderingEnvSettings) { | |||
| 162 | 163 | } | |
| 163 | 164 | } | |
| 164 | 165 | ||
| 166 | + | func (m *Manager) applySkiaEnv(settings port.RenderingEnvSettings) { | |
| 167 | + | // CPU painting threads: only set if > 0 and env var not already set | |
| 168 | + | if settings.SkiaCPUPaintingThreads > 0 && os.Getenv("WEBKIT_SKIA_CPU_PAINTING_THREADS") == "" { | |
| 169 | + | m.setEnv("WEBKIT_SKIA_CPU_PAINTING_THREADS", fmt.Sprintf("%d", settings.SkiaCPUPaintingThreads)) | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | // GPU painting threads: only set if >= 0 (0 is valid: disables GPU tile painting) | |
| 173 | + | if settings.SkiaGPUPaintingThreads >= 0 && os.Getenv("WEBKIT_SKIA_GPU_PAINTING_THREADS") == "" { | |
| 174 | + | m.setEnv("WEBKIT_SKIA_GPU_PAINTING_THREADS", fmt.Sprintf("%d", settings.SkiaGPUPaintingThreads)) | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | // CPU rendering: only set if true and env var not already set | |
| 178 | + | if settings.SkiaEnableCPURendering && os.Getenv("WEBKIT_SKIA_ENABLE_CPU_RENDERING") == "" { | |
| 179 | + | m.setEnv("WEBKIT_SKIA_ENABLE_CPU_RENDERING", "1") | |
| 180 | + | } | |
| 181 | + | } | |
| 182 | + | ||
| 165 | 183 | func (m *Manager) setEnv(key, value string) { | |
| 166 | 184 | _ = os.Setenv(key, value) | |
| 167 | 185 | m.appliedVars[key] = value | |
| @@ -54,6 +54,7 @@ func (m *Manager) ApplyEnvironment(ctx context.Context, settings port.RenderingE | |||||
| 54 | m.applyWebKitEnv(settings) | 54 | m.applyWebKitEnv(settings) | ||
| 55 | m.applyGTKEnv(settings) | 55 | m.applyGTKEnv(settings) | ||
| 56 | m.applyDebugEnv(settings) | 56 | m.applyDebugEnv(settings) | ||
| 57 | + | m.applySkiaEnv(settings) | |||
| 57 | 58 | ||||
| 58 | log.Debug(). | 59 | log.Debug(). | ||
| 59 | Interface("vars", m.appliedVars). | 60 | Interface("vars", m.appliedVars). | ||
| @@ -162,6 +163,23 @@ func (m *Manager) applyDebugEnv(settings port.RenderingEnvSettings) { | |||||
| 162 | } | 163 | } | ||
| 163 | } | 164 | } | ||
| 164 | 165 | ||||
| 166 | + | func (m *Manager) applySkiaEnv(settings port.RenderingEnvSettings) { | |||
| 167 | + | // CPU painting threads: only set if > 0 and env var not already set | |||
| 168 | + | if settings.SkiaCPUPaintingThreads > 0 && os.Getenv("WEBKIT_SKIA_CPU_PAINTING_THREADS") == "" { | |||
| 169 | + | m.setEnv("WEBKIT_SKIA_CPU_PAINTING_THREADS", fmt.Sprintf("%d", settings.SkiaCPUPaintingThreads)) | |||
| 170 | + | } | |||
| 171 | + | ||||
| 172 | + | // GPU painting threads: only set if >= 0 (0 is valid: disables GPU tile painting) | |||
| 173 | + | if settings.SkiaGPUPaintingThreads >= 0 && os.Getenv("WEBKIT_SKIA_GPU_PAINTING_THREADS") == "" { | |||
| 174 | + | m.setEnv("WEBKIT_SKIA_GPU_PAINTING_THREADS", fmt.Sprintf("%d", settings.SkiaGPUPaintingThreads)) | |||
| 175 | + | } | |||
| 176 | + | ||||
| 177 | + | // CPU rendering: only set if true and env var not already set | |||
| 178 | + | if settings.SkiaEnableCPURendering && os.Getenv("WEBKIT_SKIA_ENABLE_CPU_RENDERING") == "" { | |||
| 179 | + | m.setEnv("WEBKIT_SKIA_ENABLE_CPU_RENDERING", "1") | |||
| 180 | + | } | |||
| 181 | + | } | |||
| 182 | + | ||||
| 165 | func (m *Manager) setEnv(key, value string) { | 183 | func (m *Manager) setEnv(key, value string) { | ||
| 166 | _ = os.Setenv(key, value) | 184 | _ = os.Setenv(key, value) | ||
| 167 | m.appliedVars[key] = value | 185 | m.appliedVars[key] = value | ||
A internal/infrastructure/env/manager_test.go +172 -0
| @@ -0,0 +1,172 @@ | |||
| 1 | + | package env_test | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "context" | |
| 5 | + | "os" | |
| 6 | + | "testing" | |
| 7 | + | ||
| 8 | + | "github.com/bnema/dumber/internal/application/port" | |
| 9 | + | "github.com/bnema/dumber/internal/infrastructure/env" | |
| 10 | + | "github.com/stretchr/testify/assert" | |
| 11 | + | "github.com/stretchr/testify/require" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | func TestManager_ApplyEnvironment_SkiaEnv(t *testing.T) { | |
| 15 | + | // Save original env vars and restore after test | |
| 16 | + | origCPU := os.Getenv("WEBKIT_SKIA_CPU_PAINTING_THREADS") | |
| 17 | + | origGPU := os.Getenv("WEBKIT_SKIA_GPU_PAINTING_THREADS") | |
| 18 | + | origCPURender := os.Getenv("WEBKIT_SKIA_ENABLE_CPU_RENDERING") | |
| 19 | + | t.Cleanup(func() { | |
| 20 | + | restoreEnv("WEBKIT_SKIA_CPU_PAINTING_THREADS", origCPU) | |
| 21 | + | restoreEnv("WEBKIT_SKIA_GPU_PAINTING_THREADS", origGPU) | |
| 22 | + | restoreEnv("WEBKIT_SKIA_ENABLE_CPU_RENDERING", origCPURender) | |
| 23 | + | }) | |
| 24 | + | ||
| 25 | + | tests := []struct { | |
| 26 | + | name string | |
| 27 | + | settings port.RenderingEnvSettings | |
| 28 | + | presetEnv map[string]string | |
| 29 | + | expectedVars map[string]string | |
| 30 | + | unexpectedVars []string | |
| 31 | + | }{ | |
| 32 | + | { | |
| 33 | + | name: "CPU threads set when > 0", | |
| 34 | + | settings: port.RenderingEnvSettings{ | |
| 35 | + | SkiaCPUPaintingThreads: 4, | |
| 36 | + | SkiaGPUPaintingThreads: -1, // unset | |
| 37 | + | }, | |
| 38 | + | expectedVars: map[string]string{ | |
| 39 | + | "WEBKIT_SKIA_CPU_PAINTING_THREADS": "4", | |
| 40 | + | }, | |
| 41 | + | unexpectedVars: []string{"WEBKIT_SKIA_GPU_PAINTING_THREADS"}, | |
| 42 | + | }, | |
| 43 | + | { | |
| 44 | + | name: "GPU threads set when >= 0", | |
| 45 | + | settings: port.RenderingEnvSettings{ | |
| 46 | + | SkiaCPUPaintingThreads: 0, // unset | |
| 47 | + | SkiaGPUPaintingThreads: 2, | |
| 48 | + | }, | |
| 49 | + | expectedVars: map[string]string{ | |
| 50 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS": "2", | |
| 51 | + | }, | |
| 52 | + | unexpectedVars: []string{"WEBKIT_SKIA_CPU_PAINTING_THREADS"}, | |
| 53 | + | }, | |
| 54 | + | { | |
| 55 | + | name: "GPU threads 0 disables GPU tile painting", | |
| 56 | + | settings: port.RenderingEnvSettings{ | |
| 57 | + | SkiaGPUPaintingThreads: 0, | |
| 58 | + | }, | |
| 59 | + | expectedVars: map[string]string{ | |
| 60 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS": "0", | |
| 61 | + | }, | |
| 62 | + | }, | |
| 63 | + | { | |
| 64 | + | name: "CPU rendering enabled", | |
| 65 | + | settings: port.RenderingEnvSettings{ | |
| 66 | + | SkiaEnableCPURendering: true, | |
| 67 | + | }, | |
| 68 | + | expectedVars: map[string]string{ | |
| 69 | + | "WEBKIT_SKIA_ENABLE_CPU_RENDERING": "1", | |
| 70 | + | }, | |
| 71 | + | }, | |
| 72 | + | { | |
| 73 | + | name: "CPU rendering disabled (default)", | |
| 74 | + | settings: port.RenderingEnvSettings{ | |
| 75 | + | SkiaEnableCPURendering: false, | |
| 76 | + | }, | |
| 77 | + | unexpectedVars: []string{"WEBKIT_SKIA_ENABLE_CPU_RENDERING"}, | |
| 78 | + | }, | |
| 79 | + | { | |
| 80 | + | name: "does not override existing CPU threads env var", | |
| 81 | + | settings: port.RenderingEnvSettings{ | |
| 82 | + | SkiaCPUPaintingThreads: 8, | |
| 83 | + | }, | |
| 84 | + | presetEnv: map[string]string{ | |
| 85 | + | "WEBKIT_SKIA_CPU_PAINTING_THREADS": "2", | |
| 86 | + | }, | |
| 87 | + | expectedVars: map[string]string{ | |
| 88 | + | "WEBKIT_SKIA_CPU_PAINTING_THREADS": "2", // keeps original | |
| 89 | + | }, | |
| 90 | + | }, | |
| 91 | + | { | |
| 92 | + | name: "does not override existing GPU threads env var", | |
| 93 | + | settings: port.RenderingEnvSettings{ | |
| 94 | + | SkiaGPUPaintingThreads: 4, | |
| 95 | + | }, | |
| 96 | + | presetEnv: map[string]string{ | |
| 97 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS": "1", | |
| 98 | + | }, | |
| 99 | + | expectedVars: map[string]string{ | |
| 100 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS": "1", // keeps original | |
| 101 | + | }, | |
| 102 | + | }, | |
| 103 | + | { | |
| 104 | + | name: "does not override existing CPU rendering env var", | |
| 105 | + | settings: port.RenderingEnvSettings{ | |
| 106 | + | SkiaEnableCPURendering: true, | |
| 107 | + | }, | |
| 108 | + | presetEnv: map[string]string{ | |
| 109 | + | "WEBKIT_SKIA_ENABLE_CPU_RENDERING": "0", | |
| 110 | + | }, | |
| 111 | + | expectedVars: map[string]string{ | |
| 112 | + | "WEBKIT_SKIA_ENABLE_CPU_RENDERING": "0", // keeps original | |
| 113 | + | }, | |
| 114 | + | }, | |
| 115 | + | { | |
| 116 | + | name: "all unset by default", | |
| 117 | + | settings: port.RenderingEnvSettings{ | |
| 118 | + | SkiaCPUPaintingThreads: 0, // unset | |
| 119 | + | SkiaGPUPaintingThreads: -1, // unset | |
| 120 | + | SkiaEnableCPURendering: false, | |
| 121 | + | }, | |
| 122 | + | unexpectedVars: []string{ | |
| 123 | + | "WEBKIT_SKIA_CPU_PAINTING_THREADS", | |
| 124 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS", | |
| 125 | + | "WEBKIT_SKIA_ENABLE_CPU_RENDERING", | |
| 126 | + | }, | |
| 127 | + | }, | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | for _, tt := range tests { | |
| 131 | + | t.Run(tt.name, func(t *testing.T) { | |
| 132 | + | // Clear env vars before each test | |
| 133 | + | os.Unsetenv("WEBKIT_SKIA_CPU_PAINTING_THREADS") | |
| 134 | + | os.Unsetenv("WEBKIT_SKIA_GPU_PAINTING_THREADS") | |
| 135 | + | os.Unsetenv("WEBKIT_SKIA_ENABLE_CPU_RENDERING") | |
| 136 | + | ||
| 137 | + | // Set any preset env vars | |
| 138 | + | for k, v := range tt.presetEnv { | |
| 139 | + | os.Setenv(k, v) | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | manager := env.NewManager() | |
| 143 | + | ctx := context.Background() | |
| 144 | + | ||
| 145 | + | err := manager.ApplyEnvironment(ctx, tt.settings) | |
| 146 | + | require.NoError(t, err) | |
| 147 | + | ||
| 148 | + | // Check expected vars are set correctly | |
| 149 | + | for k, v := range tt.expectedVars { | |
| 150 | + | actual := os.Getenv(k) | |
| 151 | + | assert.Equal(t, v, actual, "env var %s", k) | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | // Check unexpected vars are not set (or were not set by us) | |
| 155 | + | appliedVars := manager.GetAppliedVars() | |
| 156 | + | for _, k := range tt.unexpectedVars { | |
| 157 | + | _, wasApplied := appliedVars[k] | |
| 158 | + | if tt.presetEnv[k] == "" { | |
| 159 | + | assert.False(t, wasApplied, "env var %s should not be applied", k) | |
| 160 | + | } | |
| 161 | + | } | |
| 162 | + | }) | |
| 163 | + | } | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | func restoreEnv(key, value string) { | |
| 167 | + | if value == "" { | |
| 168 | + | os.Unsetenv(key) | |
| 169 | + | } else { | |
| 170 | + | os.Setenv(key, value) | |
| 171 | + | } | |
| 172 | + | } | |
| @@ -0,0 +1,172 @@ | |||||
| 1 | + | package env_test | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "context" | |||
| 5 | + | "os" | |||
| 6 | + | "testing" | |||
| 7 | + | ||||
| 8 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 9 | + | "github.com/bnema/dumber/internal/infrastructure/env" | |||
| 10 | + | "github.com/stretchr/testify/assert" | |||
| 11 | + | "github.com/stretchr/testify/require" | |||
| 12 | + | ) | |||
| 13 | + | ||||
| 14 | + | func TestManager_ApplyEnvironment_SkiaEnv(t *testing.T) { | |||
| 15 | + | // Save original env vars and restore after test | |||
| 16 | + | origCPU := os.Getenv("WEBKIT_SKIA_CPU_PAINTING_THREADS") | |||
| 17 | + | origGPU := os.Getenv("WEBKIT_SKIA_GPU_PAINTING_THREADS") | |||
| 18 | + | origCPURender := os.Getenv("WEBKIT_SKIA_ENABLE_CPU_RENDERING") | |||
| 19 | + | t.Cleanup(func() { | |||
| 20 | + | restoreEnv("WEBKIT_SKIA_CPU_PAINTING_THREADS", origCPU) | |||
| 21 | + | restoreEnv("WEBKIT_SKIA_GPU_PAINTING_THREADS", origGPU) | |||
| 22 | + | restoreEnv("WEBKIT_SKIA_ENABLE_CPU_RENDERING", origCPURender) | |||
| 23 | + | }) | |||
| 24 | + | ||||
| 25 | + | tests := []struct { | |||
| 26 | + | name string | |||
| 27 | + | settings port.RenderingEnvSettings | |||
| 28 | + | presetEnv map[string]string | |||
| 29 | + | expectedVars map[string]string | |||
| 30 | + | unexpectedVars []string | |||
| 31 | + | }{ | |||
| 32 | + | { | |||
| 33 | + | name: "CPU threads set when > 0", | |||
| 34 | + | settings: port.RenderingEnvSettings{ | |||
| 35 | + | SkiaCPUPaintingThreads: 4, | |||
| 36 | + | SkiaGPUPaintingThreads: -1, // unset | |||
| 37 | + | }, | |||
| 38 | + | expectedVars: map[string]string{ | |||
| 39 | + | "WEBKIT_SKIA_CPU_PAINTING_THREADS": "4", | |||
| 40 | + | }, | |||
| 41 | + | unexpectedVars: []string{"WEBKIT_SKIA_GPU_PAINTING_THREADS"}, | |||
| 42 | + | }, | |||
| 43 | + | { | |||
| 44 | + | name: "GPU threads set when >= 0", | |||
| 45 | + | settings: port.RenderingEnvSettings{ | |||
| 46 | + | SkiaCPUPaintingThreads: 0, // unset | |||
| 47 | + | SkiaGPUPaintingThreads: 2, | |||
| 48 | + | }, | |||
| 49 | + | expectedVars: map[string]string{ | |||
| 50 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS": "2", | |||
| 51 | + | }, | |||
| 52 | + | unexpectedVars: []string{"WEBKIT_SKIA_CPU_PAINTING_THREADS"}, | |||
| 53 | + | }, | |||
| 54 | + | { | |||
| 55 | + | name: "GPU threads 0 disables GPU tile painting", | |||
| 56 | + | settings: port.RenderingEnvSettings{ | |||
| 57 | + | SkiaGPUPaintingThreads: 0, | |||
| 58 | + | }, | |||
| 59 | + | expectedVars: map[string]string{ | |||
| 60 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS": "0", | |||
| 61 | + | }, | |||
| 62 | + | }, | |||
| 63 | + | { | |||
| 64 | + | name: "CPU rendering enabled", | |||
| 65 | + | settings: port.RenderingEnvSettings{ | |||
| 66 | + | SkiaEnableCPURendering: true, | |||
| 67 | + | }, | |||
| 68 | + | expectedVars: map[string]string{ | |||
| 69 | + | "WEBKIT_SKIA_ENABLE_CPU_RENDERING": "1", | |||
| 70 | + | }, | |||
| 71 | + | }, | |||
| 72 | + | { | |||
| 73 | + | name: "CPU rendering disabled (default)", | |||
| 74 | + | settings: port.RenderingEnvSettings{ | |||
| 75 | + | SkiaEnableCPURendering: false, | |||
| 76 | + | }, | |||
| 77 | + | unexpectedVars: []string{"WEBKIT_SKIA_ENABLE_CPU_RENDERING"}, | |||
| 78 | + | }, | |||
| 79 | + | { | |||
| 80 | + | name: "does not override existing CPU threads env var", | |||
| 81 | + | settings: port.RenderingEnvSettings{ | |||
| 82 | + | SkiaCPUPaintingThreads: 8, | |||
| 83 | + | }, | |||
| 84 | + | presetEnv: map[string]string{ | |||
| 85 | + | "WEBKIT_SKIA_CPU_PAINTING_THREADS": "2", | |||
| 86 | + | }, | |||
| 87 | + | expectedVars: map[string]string{ | |||
| 88 | + | "WEBKIT_SKIA_CPU_PAINTING_THREADS": "2", // keeps original | |||
| 89 | + | }, | |||
| 90 | + | }, | |||
| 91 | + | { | |||
| 92 | + | name: "does not override existing GPU threads env var", | |||
| 93 | + | settings: port.RenderingEnvSettings{ | |||
| 94 | + | SkiaGPUPaintingThreads: 4, | |||
| 95 | + | }, | |||
| 96 | + | presetEnv: map[string]string{ | |||
| 97 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS": "1", | |||
| 98 | + | }, | |||
| 99 | + | expectedVars: map[string]string{ | |||
| 100 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS": "1", // keeps original | |||
| 101 | + | }, | |||
| 102 | + | }, | |||
| 103 | + | { | |||
| 104 | + | name: "does not override existing CPU rendering env var", | |||
| 105 | + | settings: port.RenderingEnvSettings{ | |||
| 106 | + | SkiaEnableCPURendering: true, | |||
| 107 | + | }, | |||
| 108 | + | presetEnv: map[string]string{ | |||
| 109 | + | "WEBKIT_SKIA_ENABLE_CPU_RENDERING": "0", | |||
| 110 | + | }, | |||
| 111 | + | expectedVars: map[string]string{ | |||
| 112 | + | "WEBKIT_SKIA_ENABLE_CPU_RENDERING": "0", // keeps original | |||
| 113 | + | }, | |||
| 114 | + | }, | |||
| 115 | + | { | |||
| 116 | + | name: "all unset by default", | |||
| 117 | + | settings: port.RenderingEnvSettings{ | |||
| 118 | + | SkiaCPUPaintingThreads: 0, // unset | |||
| 119 | + | SkiaGPUPaintingThreads: -1, // unset | |||
| 120 | + | SkiaEnableCPURendering: false, | |||
| 121 | + | }, | |||
| 122 | + | unexpectedVars: []string{ | |||
| 123 | + | "WEBKIT_SKIA_CPU_PAINTING_THREADS", | |||
| 124 | + | "WEBKIT_SKIA_GPU_PAINTING_THREADS", | |||
| 125 | + | "WEBKIT_SKIA_ENABLE_CPU_RENDERING", | |||
| 126 | + | }, | |||
| 127 | + | }, | |||
| 128 | + | } | |||
| 129 | + | ||||
| 130 | + | for _, tt := range tests { | |||
| 131 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 132 | + | // Clear env vars before each test | |||
| 133 | + | os.Unsetenv("WEBKIT_SKIA_CPU_PAINTING_THREADS") | |||
| 134 | + | os.Unsetenv("WEBKIT_SKIA_GPU_PAINTING_THREADS") | |||
| 135 | + | os.Unsetenv("WEBKIT_SKIA_ENABLE_CPU_RENDERING") | |||
| 136 | + | ||||
| 137 | + | // Set any preset env vars | |||
| 138 | + | for k, v := range tt.presetEnv { | |||
| 139 | + | os.Setenv(k, v) | |||
| 140 | + | } | |||
| 141 | + | ||||
| 142 | + | manager := env.NewManager() | |||
| 143 | + | ctx := context.Background() | |||
| 144 | + | ||||
| 145 | + | err := manager.ApplyEnvironment(ctx, tt.settings) | |||
| 146 | + | require.NoError(t, err) | |||
| 147 | + | ||||
| 148 | + | // Check expected vars are set correctly | |||
| 149 | + | for k, v := range tt.expectedVars { | |||
| 150 | + | actual := os.Getenv(k) | |||
| 151 | + | assert.Equal(t, v, actual, "env var %s", k) | |||
| 152 | + | } | |||
| 153 | + | ||||
| 154 | + | // Check unexpected vars are not set (or were not set by us) | |||
| 155 | + | appliedVars := manager.GetAppliedVars() | |||
| 156 | + | for _, k := range tt.unexpectedVars { | |||
| 157 | + | _, wasApplied := appliedVars[k] | |||
| 158 | + | if tt.presetEnv[k] == "" { | |||
| 159 | + | assert.False(t, wasApplied, "env var %s should not be applied", k) | |||
| 160 | + | } | |||
| 161 | + | } | |||
| 162 | + | }) | |||
| 163 | + | } | |||
| 164 | + | } | |||
| 165 | + | ||||
| 166 | + | func restoreEnv(key, value string) { | |||
| 167 | + | if value == "" { | |||
| 168 | + | os.Unsetenv(key) | |||
| 169 | + | } else { | |||
| 170 | + | os.Setenv(key, value) | |||
| 171 | + | } | |||
| 172 | + | } | |||
M internal/infrastructure/webkit/context.go +64 -9
| @@ -6,6 +6,7 @@ import ( | |||
| 6 | 6 | "path/filepath" | |
| 7 | 7 | "sync" | |
| 8 | 8 | ||
| 9 | + | "github.com/bnema/dumber/internal/application/port" | |
| 9 | 10 | "github.com/bnema/dumber/internal/logging" | |
| 10 | 11 | "github.com/bnema/puregotk-webkit/webkit" | |
| 11 | 12 | "github.com/rs/zerolog" | |
| @@ -34,31 +35,66 @@ type WebKitContext struct { | |||
| 34 | 35 | // The dataDir and cacheDir are used for cookie storage, cache, and other persistent data. | |
| 35 | 36 | // This MUST be called before creating any WebViews to ensure they use persistent storage. | |
| 36 | 37 | func NewWebKitContext(ctx context.Context, dataDir, cacheDir string) (*WebKitContext, error) { | |
| 38 | + | return NewWebKitContextWithOptions(ctx, port.WebKitContextOptions{ | |
| 39 | + | DataDir: dataDir, | |
| 40 | + | CacheDir: cacheDir, | |
| 41 | + | }) | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | // NewWebKitContextWithOptions creates and initializes a WebKitContext with the given options. | |
| 45 | + | // This allows configuring memory pressure settings for web and network processes. | |
| 46 | + | func NewWebKitContextWithOptions(ctx context.Context, opts port.WebKitContextOptions) (*WebKitContext, error) { | |
| 37 | 47 | log := logging.FromContext(ctx).With().Str("component", "webkit-context").Logger() | |
| 38 | 48 | ||
| 39 | - | if dataDir == "" { | |
| 49 | + | if opts.DataDir == "" { | |
| 40 | 50 | return nil, fmt.Errorf("data directory cannot be empty") | |
| 41 | 51 | } | |
| 42 | - | if cacheDir == "" { | |
| 52 | + | if opts.CacheDir == "" { | |
| 43 | 53 | return nil, fmt.Errorf("cache directory cannot be empty") | |
| 44 | 54 | } | |
| 45 | 55 | ||
| 46 | 56 | wkCtx := &WebKitContext{ | |
| 47 | - | dataDir: dataDir, | |
| 48 | - | cacheDir: cacheDir, | |
| 57 | + | dataDir: opts.DataDir, | |
| 58 | + | cacheDir: opts.CacheDir, | |
| 49 | 59 | logger: log, | |
| 50 | 60 | } | |
| 51 | 61 | ||
| 62 | + | // Apply network process memory pressure settings BEFORE creating session | |
| 63 | + | // Per WebKitGTK docs: must be called before creating any NetworkSession | |
| 64 | + | if opts.IsNetworkProcessMemoryConfigured() { | |
| 65 | + | applier := NewMemoryPressureApplier() | |
| 66 | + | if err := applier.ApplyNetworkProcessSettings(ctx, opts.NetworkProcessMemory); err != nil { | |
| 67 | + | log.Warn().Err(err).Msg("failed to apply network process memory pressure settings") | |
| 68 | + | } | |
| 69 | + | } | |
| 70 | + | ||
| 52 | 71 | // Create persistent network session FIRST | |
| 53 | 72 | // Per WebKitGTK 6.0 docs: "The first WebKitNetworkSession created becomes the default" | |
| 54 | 73 | if err := wkCtx.initNetworkSession(); err != nil { | |
| 55 | 74 | return nil, fmt.Errorf("failed to init network session: %w", err) | |
| 56 | 75 | } | |
| 57 | 76 | ||
| 58 | - | // Get or create WebContext | |
| 59 | - | wkCtx.webContext = webkit.WebContextGetDefault() | |
| 77 | + | // Create WebContext - with memory pressure settings if configured | |
| 78 | + | if opts.IsWebProcessMemoryConfigured() { | |
| 79 | + | applier := NewMemoryPressureApplier() | |
| 80 | + | settings, err := applier.ApplyWebProcessSettings(ctx, opts.WebProcessMemory) | |
| 81 | + | if err != nil { | |
| 82 | + | log.Warn().Err(err).Msg("failed to prepare web process memory pressure settings") | |
| 83 | + | } | |
| 84 | + | if settings != nil { | |
| 85 | + | wkCtx.webContext = createWebContextWithMemorySettings(settings.(*webkit.MemoryPressureSettings)) | |
| 86 | + | if wkCtx.webContext != nil { | |
| 87 | + | log.Debug().Msg("created WebContext with memory pressure settings") | |
| 88 | + | } | |
| 89 | + | } | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | // Fall back to default WebContext if not created above | |
| 60 | 93 | if wkCtx.webContext == nil { | |
| 61 | - | wkCtx.webContext = webkit.NewWebContext() | |
| 94 | + | wkCtx.webContext = webkit.WebContextGetDefault() | |
| 95 | + | if wkCtx.webContext == nil { | |
| 96 | + | wkCtx.webContext = webkit.NewWebContext() | |
| 97 | + | } | |
| 62 | 98 | } | |
| 63 | 99 | if wkCtx.webContext == nil { | |
| 64 | 100 | return nil, fmt.Errorf("failed to get or create WebContext") | |
| @@ -69,13 +105,32 @@ func NewWebKitContext(ctx context.Context, dataDir, cacheDir string) (*WebKitCon | |||
| 69 | 105 | ||
| 70 | 106 | wkCtx.initialized = true | |
| 71 | 107 | log.Info(). | |
| 72 | - | Str("data_dir", dataDir). | |
| 73 | - | Str("cache_dir", cacheDir). | |
| 108 | + | Str("data_dir", opts.DataDir). | |
| 109 | + | Str("cache_dir", opts.CacheDir). | |
| 74 | 110 | Msg("webkit context initialized") | |
| 75 | 111 | ||
| 76 | 112 | return wkCtx, nil | |
| 77 | 113 | } | |
| 78 | 114 | ||
| 115 | + | // createWebContextWithMemorySettings creates a WebContext with memory-pressure-settings. | |
| 116 | + | // The memory-pressure-settings property is construct-only, so we need to set it at creation time. | |
| 117 | + | func createWebContextWithMemorySettings(settings *webkit.MemoryPressureSettings) *webkit.WebContext { | |
| 118 | + | if settings == nil { | |
| 119 | + | return nil | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | // Create a new WebContext and set memory pressure settings | |
| 123 | + | // Note: memory-pressure-settings is construct-only, but we try setting it via property | |
| 124 | + | // as an alternative to g_object_new_with_properties which may not work reliably | |
| 125 | + | ctx := webkit.NewWebContext() | |
| 126 | + | if ctx != nil { | |
| 127 | + | // Try to set the property - this may not work for construct-only properties | |
| 128 | + | // but it's the best we can do without lower-level GObject access | |
| 129 | + | ctx.SetPropertyMemoryPressureSettings(settings.GoPointer()) | |
| 130 | + | } | |
| 131 | + | return ctx | |
| 132 | + | } | |
| 133 | + | ||
| 79 | 134 | // initNetworkSession creates and configures the persistent network session. | |
| 80 | 135 | func (c *WebKitContext) initNetworkSession() error { | |
| 81 | 136 | // Create persistent network session | |
| @@ -6,6 +6,7 @@ import ( | |||||
| 6 | "path/filepath" | 6 | "path/filepath" | ||
| 7 | "sync" | 7 | "sync" | ||
| 8 | 8 | ||||
| 9 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 9 | "github.com/bnema/dumber/internal/logging" | 10 | "github.com/bnema/dumber/internal/logging" | ||
| 10 | "github.com/bnema/puregotk-webkit/webkit" | 11 | "github.com/bnema/puregotk-webkit/webkit" | ||
| 11 | "github.com/rs/zerolog" | 12 | "github.com/rs/zerolog" | ||
| @@ -34,31 +35,66 @@ type WebKitContext struct { | |||||
| 34 | // The dataDir and cacheDir are used for cookie storage, cache, and other persistent data. | 35 | // The dataDir and cacheDir are used for cookie storage, cache, and other persistent data. | ||
| 35 | // This MUST be called before creating any WebViews to ensure they use persistent storage. | 36 | // This MUST be called before creating any WebViews to ensure they use persistent storage. | ||
| 36 | func NewWebKitContext(ctx context.Context, dataDir, cacheDir string) (*WebKitContext, error) { | 37 | func NewWebKitContext(ctx context.Context, dataDir, cacheDir string) (*WebKitContext, error) { | ||
| 38 | + | return NewWebKitContextWithOptions(ctx, port.WebKitContextOptions{ | |||
| 39 | + | DataDir: dataDir, | |||
| 40 | + | CacheDir: cacheDir, | |||
| 41 | + | }) | |||
| 42 | + | } | |||
| 43 | + | ||||
| 44 | + | // NewWebKitContextWithOptions creates and initializes a WebKitContext with the given options. | |||
| 45 | + | // This allows configuring memory pressure settings for web and network processes. | |||
| 46 | + | func NewWebKitContextWithOptions(ctx context.Context, opts port.WebKitContextOptions) (*WebKitContext, error) { | |||
| 37 | log := logging.FromContext(ctx).With().Str("component", "webkit-context").Logger() | 47 | log := logging.FromContext(ctx).With().Str("component", "webkit-context").Logger() | ||
| 38 | 48 | ||||
| 39 | - | if dataDir == "" { | 49 | + | if opts.DataDir == "" { |
| 40 | return nil, fmt.Errorf("data directory cannot be empty") | 50 | return nil, fmt.Errorf("data directory cannot be empty") | ||
| 41 | } | 51 | } | ||
| 42 | - | if cacheDir == "" { | 52 | + | if opts.CacheDir == "" { |
| 43 | return nil, fmt.Errorf("cache directory cannot be empty") | 53 | return nil, fmt.Errorf("cache directory cannot be empty") | ||
| 44 | } | 54 | } | ||
| 45 | 55 | ||||
| 46 | wkCtx := &WebKitContext{ | 56 | wkCtx := &WebKitContext{ | ||
| 47 | - | dataDir: dataDir, | 57 | + | dataDir: opts.DataDir, |
| 48 | - | cacheDir: cacheDir, | 58 | + | cacheDir: opts.CacheDir, |
| 49 | logger: log, | 59 | logger: log, | ||
| 50 | } | 60 | } | ||
| 51 | 61 | ||||
| 62 | + | // Apply network process memory pressure settings BEFORE creating session | |||
| 63 | + | // Per WebKitGTK docs: must be called before creating any NetworkSession | |||
| 64 | + | if opts.IsNetworkProcessMemoryConfigured() { | |||
| 65 | + | applier := NewMemoryPressureApplier() | |||
| 66 | + | if err := applier.ApplyNetworkProcessSettings(ctx, opts.NetworkProcessMemory); err != nil { | |||
| 67 | + | log.Warn().Err(err).Msg("failed to apply network process memory pressure settings") | |||
| 68 | + | } | |||
| 69 | + | } | |||
| 70 | + | ||||
| 52 | // Create persistent network session FIRST | 71 | // Create persistent network session FIRST | ||
| 53 | // Per WebKitGTK 6.0 docs: "The first WebKitNetworkSession created becomes the default" | 72 | // Per WebKitGTK 6.0 docs: "The first WebKitNetworkSession created becomes the default" | ||
| 54 | if err := wkCtx.initNetworkSession(); err != nil { | 73 | if err := wkCtx.initNetworkSession(); err != nil { | ||
| 55 | return nil, fmt.Errorf("failed to init network session: %w", err) | 74 | return nil, fmt.Errorf("failed to init network session: %w", err) | ||
| 56 | } | 75 | } | ||
| 57 | 76 | ||||
| 58 | - | // Get or create WebContext | 77 | + | // Create WebContext - with memory pressure settings if configured |
| 59 | - | wkCtx.webContext = webkit.WebContextGetDefault() | 78 | + | if opts.IsWebProcessMemoryConfigured() { |
| 79 | + | applier := NewMemoryPressureApplier() | |||
| 80 | + | settings, err := applier.ApplyWebProcessSettings(ctx, opts.WebProcessMemory) | |||
| 81 | + | if err != nil { | |||
| 82 | + | log.Warn().Err(err).Msg("failed to prepare web process memory pressure settings") | |||
| 83 | + | } | |||
| 84 | + | if settings != nil { | |||
| 85 | + | wkCtx.webContext = createWebContextWithMemorySettings(settings.(*webkit.MemoryPressureSettings)) | |||
| 86 | + | if wkCtx.webContext != nil { | |||
| 87 | + | log.Debug().Msg("created WebContext with memory pressure settings") | |||
| 88 | + | } | |||
| 89 | + | } | |||
| 90 | + | } | |||
| 91 | + | ||||
| 92 | + | // Fall back to default WebContext if not created above | |||
| 60 | if wkCtx.webContext == nil { | 93 | if wkCtx.webContext == nil { | ||
| 61 | - | wkCtx.webContext = webkit.NewWebContext() | 94 | + | wkCtx.webContext = webkit.WebContextGetDefault() |
| 95 | + | if wkCtx.webContext == nil { | |||
| 96 | + | wkCtx.webContext = webkit.NewWebContext() | |||
| 97 | + | } | |||
| 62 | } | 98 | } | ||
| 63 | if wkCtx.webContext == nil { | 99 | if wkCtx.webContext == nil { | ||
| 64 | return nil, fmt.Errorf("failed to get or create WebContext") | 100 | return nil, fmt.Errorf("failed to get or create WebContext") | ||
| @@ -69,13 +105,32 @@ func NewWebKitContext(ctx context.Context, dataDir, cacheDir string) (*WebKitCon | |||||
| 69 | 105 | ||||
| 70 | wkCtx.initialized = true | 106 | wkCtx.initialized = true | ||
| 71 | log.Info(). | 107 | log.Info(). | ||
| 72 | - | Str("data_dir", dataDir). | 108 | + | Str("data_dir", opts.DataDir). |
| 73 | - | Str("cache_dir", cacheDir). | 109 | + | Str("cache_dir", opts.CacheDir). |
| 74 | Msg("webkit context initialized") | 110 | Msg("webkit context initialized") | ||
| 75 | 111 | ||||
| 76 | return wkCtx, nil | 112 | return wkCtx, nil | ||
| 77 | } | 113 | } | ||
| 78 | 114 | ||||
| 115 | + | // createWebContextWithMemorySettings creates a WebContext with memory-pressure-settings. | |||
| 116 | + | // The memory-pressure-settings property is construct-only, so we need to set it at creation time. | |||
| 117 | + | func createWebContextWithMemorySettings(settings *webkit.MemoryPressureSettings) *webkit.WebContext { | |||
| 118 | + | if settings == nil { | |||
| 119 | + | return nil | |||
| 120 | + | } | |||
| 121 | + | ||||
| 122 | + | // Create a new WebContext and set memory pressure settings | |||
| 123 | + | // Note: memory-pressure-settings is construct-only, but we try setting it via property | |||
| 124 | + | // as an alternative to g_object_new_with_properties which may not work reliably | |||
| 125 | + | ctx := webkit.NewWebContext() | |||
| 126 | + | if ctx != nil { | |||
| 127 | + | // Try to set the property - this may not work for construct-only properties | |||
| 128 | + | // but it's the best we can do without lower-level GObject access | |||
| 129 | + | ctx.SetPropertyMemoryPressureSettings(settings.GoPointer()) | |||
| 130 | + | } | |||
| 131 | + | return ctx | |||
| 132 | + | } | |||
| 133 | + | ||||
| 79 | // initNetworkSession creates and configures the persistent network session. | 134 | // initNetworkSession creates and configures the persistent network session. | ||
| 80 | func (c *WebKitContext) initNetworkSession() error { | 135 | func (c *WebKitContext) initNetworkSession() error { | ||
| 81 | // Create persistent network session | 136 | // Create persistent network session | ||
A internal/infrastructure/webkit/memory_pressure.go +108 -0
| @@ -0,0 +1,108 @@ | |||
| 1 | + | package webkit | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "context" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/dumber/internal/application/port" | |
| 7 | + | "github.com/bnema/dumber/internal/logging" | |
| 8 | + | "github.com/bnema/puregotk-webkit/webkit" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // MemoryPressureApplier implements port.MemoryPressureApplier for WebKitGTK. | |
| 12 | + | type MemoryPressureApplier struct{} | |
| 13 | + | ||
| 14 | + | // NewMemoryPressureApplier creates a new MemoryPressureApplier. | |
| 15 | + | func NewMemoryPressureApplier() *MemoryPressureApplier { | |
| 16 | + | return &MemoryPressureApplier{} | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | // ApplyNetworkProcessSettings applies memory pressure settings to the network process. | |
| 20 | + | // Must be called BEFORE creating any NetworkSession. | |
| 21 | + | func (*MemoryPressureApplier) ApplyNetworkProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) error { | |
| 22 | + | log := logging.FromContext(ctx) | |
| 23 | + | ||
| 24 | + | if cfg == nil || !cfg.IsConfigured() { | |
| 25 | + | log.Debug().Msg("no network process memory pressure settings configured") | |
| 26 | + | return nil | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | settings := buildMemoryPressureSettings(cfg) | |
| 30 | + | if settings == nil { | |
| 31 | + | return nil | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | webkit.NetworkSessionSetMemoryPressureSettings(settings) | |
| 35 | + | log.Info(). | |
| 36 | + | Int("limit_mb", cfg.MemoryLimitMB). | |
| 37 | + | Float64("poll_sec", cfg.PollIntervalSec). | |
| 38 | + | Float64("conservative", cfg.ConservativeThreshold). | |
| 39 | + | Float64("strict", cfg.StrictThreshold). | |
| 40 | + | Float64("kill", cfg.KillThreshold). | |
| 41 | + | Msg("applied network process memory pressure settings") | |
| 42 | + | ||
| 43 | + | return nil | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | // ApplyWebProcessSettings applies memory pressure settings to web processes. | |
| 47 | + | // Returns an opaque settings object that should be passed to WebContext creation. | |
| 48 | + | // Returns nil if no settings are configured. | |
| 49 | + | func (*MemoryPressureApplier) ApplyWebProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error) { | |
| 50 | + | log := logging.FromContext(ctx) | |
| 51 | + | ||
| 52 | + | if cfg == nil || !cfg.IsConfigured() { | |
| 53 | + | log.Debug().Msg("no web process memory pressure settings configured") | |
| 54 | + | return nil, nil | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | settings := buildMemoryPressureSettings(cfg) | |
| 58 | + | if settings == nil { | |
| 59 | + | return nil, nil | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | log.Info(). | |
| 63 | + | Int("limit_mb", cfg.MemoryLimitMB). | |
| 64 | + | Float64("poll_sec", cfg.PollIntervalSec). | |
| 65 | + | Float64("conservative", cfg.ConservativeThreshold). | |
| 66 | + | Float64("strict", cfg.StrictThreshold). | |
| 67 | + | Float64("kill", cfg.KillThreshold). | |
| 68 | + | Msg("prepared web process memory pressure settings") | |
| 69 | + | ||
| 70 | + | return settings, nil | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | // buildMemoryPressureSettings creates WebKitMemoryPressureSettings from config. | |
| 74 | + | // Returns nil if no settings are configured. | |
| 75 | + | func buildMemoryPressureSettings(cfg *port.MemoryPressureConfig) *webkit.MemoryPressureSettings { | |
| 76 | + | if cfg == nil || !cfg.IsConfigured() { | |
| 77 | + | return nil | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | settings := webkit.NewMemoryPressureSettings() | |
| 81 | + | ||
| 82 | + | if cfg.MemoryLimitMB > 0 { | |
| 83 | + | settings.SetMemoryLimit(uint(cfg.MemoryLimitMB)) | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | if cfg.PollIntervalSec > 0 { | |
| 87 | + | settings.SetPollInterval(cfg.PollIntervalSec) | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | // Thresholds must be in (0, 1) | |
| 91 | + | if cfg.ConservativeThreshold > 0 && cfg.ConservativeThreshold < 1 { | |
| 92 | + | settings.SetConservativeThreshold(cfg.ConservativeThreshold) | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | if cfg.StrictThreshold > 0 && cfg.StrictThreshold < 1 { | |
| 96 | + | settings.SetStrictThreshold(cfg.StrictThreshold) | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | // Kill threshold: -1 = unset, 0 = never kill, >0 = threshold | |
| 100 | + | if cfg.KillThreshold >= 0 { | |
| 101 | + | settings.SetKillThreshold(cfg.KillThreshold) | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | return settings | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | // Ensure MemoryPressureApplier implements the interface. | |
| 108 | + | var _ port.MemoryPressureApplier = (*MemoryPressureApplier)(nil) | |
| @@ -0,0 +1,108 @@ | |||||
| 1 | + | package webkit | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "context" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 7 | + | "github.com/bnema/dumber/internal/logging" | |||
| 8 | + | "github.com/bnema/puregotk-webkit/webkit" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // MemoryPressureApplier implements port.MemoryPressureApplier for WebKitGTK. | |||
| 12 | + | type MemoryPressureApplier struct{} | |||
| 13 | + | ||||
| 14 | + | // NewMemoryPressureApplier creates a new MemoryPressureApplier. | |||
| 15 | + | func NewMemoryPressureApplier() *MemoryPressureApplier { | |||
| 16 | + | return &MemoryPressureApplier{} | |||
| 17 | + | } | |||
| 18 | + | ||||
| 19 | + | // ApplyNetworkProcessSettings applies memory pressure settings to the network process. | |||
| 20 | + | // Must be called BEFORE creating any NetworkSession. | |||
| 21 | + | func (*MemoryPressureApplier) ApplyNetworkProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) error { | |||
| 22 | + | log := logging.FromContext(ctx) | |||
| 23 | + | ||||
| 24 | + | if cfg == nil || !cfg.IsConfigured() { | |||
| 25 | + | log.Debug().Msg("no network process memory pressure settings configured") | |||
| 26 | + | return nil | |||
| 27 | + | } | |||
| 28 | + | ||||
| 29 | + | settings := buildMemoryPressureSettings(cfg) | |||
| 30 | + | if settings == nil { | |||
| 31 | + | return nil | |||
| 32 | + | } | |||
| 33 | + | ||||
| 34 | + | webkit.NetworkSessionSetMemoryPressureSettings(settings) | |||
| 35 | + | log.Info(). | |||
| 36 | + | Int("limit_mb", cfg.MemoryLimitMB). | |||
| 37 | + | Float64("poll_sec", cfg.PollIntervalSec). | |||
| 38 | + | Float64("conservative", cfg.ConservativeThreshold). | |||
| 39 | + | Float64("strict", cfg.StrictThreshold). | |||
| 40 | + | Float64("kill", cfg.KillThreshold). | |||
| 41 | + | Msg("applied network process memory pressure settings") | |||
| 42 | + | ||||
| 43 | + | return nil | |||
| 44 | + | } | |||
| 45 | + | ||||
| 46 | + | // ApplyWebProcessSettings applies memory pressure settings to web processes. | |||
| 47 | + | // Returns an opaque settings object that should be passed to WebContext creation. | |||
| 48 | + | // Returns nil if no settings are configured. | |||
| 49 | + | func (*MemoryPressureApplier) ApplyWebProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error) { | |||
| 50 | + | log := logging.FromContext(ctx) | |||
| 51 | + | ||||
| 52 | + | if cfg == nil || !cfg.IsConfigured() { | |||
| 53 | + | log.Debug().Msg("no web process memory pressure settings configured") | |||
| 54 | + | return nil, nil | |||
| 55 | + | } | |||
| 56 | + | ||||
| 57 | + | settings := buildMemoryPressureSettings(cfg) | |||
| 58 | + | if settings == nil { | |||
| 59 | + | return nil, nil | |||
| 60 | + | } | |||
| 61 | + | ||||
| 62 | + | log.Info(). | |||
| 63 | + | Int("limit_mb", cfg.MemoryLimitMB). | |||
| 64 | + | Float64("poll_sec", cfg.PollIntervalSec). | |||
| 65 | + | Float64("conservative", cfg.ConservativeThreshold). | |||
| 66 | + | Float64("strict", cfg.StrictThreshold). | |||
| 67 | + | Float64("kill", cfg.KillThreshold). | |||
| 68 | + | Msg("prepared web process memory pressure settings") | |||
| 69 | + | ||||
| 70 | + | return settings, nil | |||
| 71 | + | } | |||
| 72 | + | ||||
| 73 | + | // buildMemoryPressureSettings creates WebKitMemoryPressureSettings from config. | |||
| 74 | + | // Returns nil if no settings are configured. | |||
| 75 | + | func buildMemoryPressureSettings(cfg *port.MemoryPressureConfig) *webkit.MemoryPressureSettings { | |||
| 76 | + | if cfg == nil || !cfg.IsConfigured() { | |||
| 77 | + | return nil | |||
| 78 | + | } | |||
| 79 | + | ||||
| 80 | + | settings := webkit.NewMemoryPressureSettings() | |||
| 81 | + | ||||
| 82 | + | if cfg.MemoryLimitMB > 0 { | |||
| 83 | + | settings.SetMemoryLimit(uint(cfg.MemoryLimitMB)) | |||
| 84 | + | } | |||
| 85 | + | ||||
| 86 | + | if cfg.PollIntervalSec > 0 { | |||
| 87 | + | settings.SetPollInterval(cfg.PollIntervalSec) | |||
| 88 | + | } | |||
| 89 | + | ||||
| 90 | + | // Thresholds must be in (0, 1) | |||
| 91 | + | if cfg.ConservativeThreshold > 0 && cfg.ConservativeThreshold < 1 { | |||
| 92 | + | settings.SetConservativeThreshold(cfg.ConservativeThreshold) | |||
| 93 | + | } | |||
| 94 | + | ||||
| 95 | + | if cfg.StrictThreshold > 0 && cfg.StrictThreshold < 1 { | |||
| 96 | + | settings.SetStrictThreshold(cfg.StrictThreshold) | |||
| 97 | + | } | |||
| 98 | + | ||||
| 99 | + | // Kill threshold: -1 = unset, 0 = never kill, >0 = threshold | |||
| 100 | + | if cfg.KillThreshold >= 0 { | |||
| 101 | + | settings.SetKillThreshold(cfg.KillThreshold) | |||
| 102 | + | } | |||
| 103 | + | ||||
| 104 | + | return settings | |||
| 105 | + | } | |||
| 106 | + | ||||
| 107 | + | // Ensure MemoryPressureApplier implements the interface. | |||
| 108 | + | var _ port.MemoryPressureApplier = (*MemoryPressureApplier)(nil) | |||