mirror of
https://github.com/semihalev/twig.git
synced 2026-03-14 13:55:46 +01:00
154 lines
3.6 KiB
Go
154 lines
3.6 KiB
Go
package twig
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// Struct for testing attribute cache
|
|
type testType struct {
|
|
Field1 string
|
|
Field2 int
|
|
Field3 bool
|
|
}
|
|
|
|
func (t *testType) Method1() string {
|
|
return t.Field1
|
|
}
|
|
|
|
func (t testType) Method2() int {
|
|
return t.Field2
|
|
}
|
|
|
|
// Create many distinct types to stress the attribute cache
|
|
type dynamicType struct {
|
|
name string
|
|
fields map[string]interface{}
|
|
}
|
|
|
|
// Custom interface to showcase reflection
|
|
type displayable interface {
|
|
Display() string
|
|
}
|
|
|
|
func (d dynamicType) Display() string {
|
|
return fmt.Sprintf("Type: %s", d.name)
|
|
}
|
|
|
|
// Benchmark the attribute cache with a small number of types
|
|
func BenchmarkAttributeCache_FewTypes(b *testing.B) {
|
|
// Reset the attribute cache
|
|
attributeCache.Lock()
|
|
attributeCache.m = make(map[attributeCacheKey]attributeCacheEntry)
|
|
attributeCache.currSize = 0
|
|
attributeCache.Unlock()
|
|
|
|
// Create a render context
|
|
ctx := NewRenderContext(nil, nil, nil)
|
|
defer ctx.Release()
|
|
|
|
obj := &testType{
|
|
Field1: "test",
|
|
Field2: 123,
|
|
Field3: true,
|
|
}
|
|
|
|
// Run the benchmark
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Access different fields and methods
|
|
_, _ = ctx.getAttribute(obj, "Field1")
|
|
_, _ = ctx.getAttribute(obj, "Field2")
|
|
_, _ = ctx.getAttribute(obj, "Field3")
|
|
_, _ = ctx.getAttribute(obj, "Method1")
|
|
_, _ = ctx.getAttribute(obj, "Method2")
|
|
}
|
|
}
|
|
|
|
// Benchmark the attribute cache with many different types
|
|
func BenchmarkAttributeCache_ManyTypes(b *testing.B) {
|
|
// Reset the attribute cache
|
|
attributeCache.Lock()
|
|
attributeCache.m = make(map[attributeCacheKey]attributeCacheEntry)
|
|
attributeCache.currSize = 0
|
|
attributeCache.Unlock()
|
|
|
|
// Create a render context
|
|
ctx := NewRenderContext(nil, nil, nil)
|
|
defer ctx.Release()
|
|
|
|
// Create 2000 different types (more than the cache limit)
|
|
types := make([]interface{}, 2000)
|
|
for i := 0; i < 2000; i++ {
|
|
types[i] = dynamicType{
|
|
name: fmt.Sprintf("Type%d", i),
|
|
fields: map[string]interface{}{
|
|
"field1": fmt.Sprintf("value%d", i),
|
|
"field2": i,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Run the benchmark
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Access attributes across different types
|
|
typeIdx := i % 2000
|
|
_, _ = ctx.getAttribute(types[typeIdx], "name")
|
|
_, _ = ctx.getAttribute(types[typeIdx], "fields")
|
|
}
|
|
}
|
|
|
|
// Verify that the attribute cache properly performs LRU eviction
|
|
func TestAttributeCacheLRUEviction(t *testing.T) {
|
|
// Reset the attribute cache
|
|
attributeCache.Lock()
|
|
attributeCache.m = make(map[attributeCacheKey]attributeCacheEntry)
|
|
attributeCache.currSize = 0
|
|
attributeCache.maxSize = 10 // Small size for testing
|
|
attributeCache.Unlock()
|
|
|
|
// Create a render context
|
|
ctx := NewRenderContext(nil, nil, nil)
|
|
defer ctx.Release()
|
|
|
|
// Create 20 different types (more than the cache size)
|
|
types := make([]interface{}, 20)
|
|
for i := 0; i < 20; i++ {
|
|
types[i] = dynamicType{
|
|
name: fmt.Sprintf("Type%d", i),
|
|
fields: map[string]interface{}{
|
|
"field1": fmt.Sprintf("value%d", i),
|
|
},
|
|
}
|
|
}
|
|
|
|
// First access all types once
|
|
for i := 0; i < 20; i++ {
|
|
_, _ = ctx.getAttribute(types[i], "name")
|
|
}
|
|
|
|
// Now access the last 5 types more frequently
|
|
for i := 0; i < 100; i++ {
|
|
typeIdx := 15 + (i % 5) // Types 15-19
|
|
_, _ = ctx.getAttribute(types[typeIdx], "name")
|
|
}
|
|
|
|
// Check which types are in the cache
|
|
attributeCache.RLock()
|
|
defer attributeCache.RUnlock()
|
|
|
|
// The most recently/frequently used types should be in the cache
|
|
for i := 15; i < 20; i++ {
|
|
typeKey := attributeCacheKey{
|
|
typ: reflect.TypeOf(types[i]),
|
|
attr: "name",
|
|
}
|
|
|
|
_, found := attributeCache.m[typeKey]
|
|
if !found {
|
|
t.Errorf("Expected type %d to be in cache, but it wasn't", i)
|
|
}
|
|
}
|
|
}
|