Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/Leaf/LeafEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ extension LeafEncoder {
}

private final class KeyedContainerImpl<Key>: KeyedEncodingContainerProtocol, LeafEncodingResolvable where Key: CodingKey {
private let encoder: EncoderImpl
private unowned let encoder: EncoderImpl
private var data: [String: any LeafEncodingResolvable] = [:]
private var nestedEncoderCaptures: [AnyObject] = []

Expand Down Expand Up @@ -230,7 +230,7 @@ extension LeafEncoder {
}

private final class UnkeyedContainerImpl: UnkeyedEncodingContainer, LeafEncodingResolvable {
private let encoder: EncoderImpl
private unowned let encoder: EncoderImpl
private var data: [any LeafEncodingResolvable] = []
private var nestedEncoderCaptures: [AnyObject] = []

Expand Down
50 changes: 50 additions & 0 deletions Tests/LeafTests/LeafMemoryGrowthTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import XCTest
import Leaf
import LeafKit
import XCTVapor

/*
to profile memory growth, use an sh script like this:
```bash
#!/bin/zsh
swift test --filter LeafMemoryGrowthTests &
sleep 5
PID=$(ps aux | grep '[l]eafPackageTests' | awk '{print $2}' | head -n1)
echo "leafPackageTests PID: $PID"
leaks $PID
```
*/
final class LeafMemoryGrowthTests: XCTestCase {
func testRepeatedRenderMemoryGrowth() async throws {
sleep(3) // Keep process alive for leaks profiling
var test = TestFiles()
test.files["/foo.leaf"] = "Hello #(name)!"

try await withApp { app in
app.views.use(.leaf)
app.leaf.sources = .singleSource(test)
struct Foo: Encodable { var name: String }
// Render with context 1000 times
for _ in 0..<1000 {
_ = try await app.leaf.renderer.render(path: "foo", context: Foo(name: "World")).get()
}
}
sleep(10) // Keep process alive for leaks profiling
}

func testRepeatedRenderNoContextMemoryGrowth() async throws {
sleep(3) // Keep process alive for leaks profiling
var test = TestFiles()
test.files["/foo.leaf"] = "Hello!"

try await withApp { app in
app.views.use(.leaf)
app.leaf.sources = .singleSource(test)
// Render without context 1000 times (pass nil context)
for _ in 0..<1000 {
_ = try await app.leaf.renderer.render(path: "foo", context: Optional<Bool>.none).get()
}
}
sleep(10) // Keep process alive for leaks profiling
}
}
Loading