* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* openFuyao is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package analyzer
import (
"math"
"testing"
"time"
)
type testRuleEngine struct{}
func (testRuleEngine) Evaluate(metrics NodeMetrics) []Detection {
if metrics.LLCMissRate > 0.15 {
return []Detection{{Type: "cache-pollution", Message: "cache"}}
}
return nil
}
type testCalculator struct{}
func (testCalculator) Compute(metrics NodeMetrics) float64 {
return metrics.LLCMissRate + metrics.LLCOccupancy
}
func TestNodeStateStoreSmoothsSamplesAndConsumesDirtyReport(t *testing.T) {
store := NewNodeStateStore(10*time.Second, time.Minute, testRuleEngine{}, testCalculator{})
base := time.Now().UTC()
store.Add("node-b", base, NodeMetrics{LLCMissRate: 0.10, LLCOccupancy: 0.20, PSIAvailable: true}, "ok")
report := store.AddWithMetadata("node-b", base.Add(time.Second), NodeMetrics{
LLCMissRate: 0.30,
LLCOccupancy: 0.40,
PSIAvailable: false,
PSIIOAvg10: 2,
PSIMemAvg10: 4,
MemAvailRatio: 0.8,
PageScanRate: 10,
}, "partial", ReportMetadata{
Sequence: 7,
Availability: []MetricAvailability{{Collector: "psi", Metric: "psiIoSome", Available: true}},
})
if report.SampleCount != 2 {
t.Fatalf("sample count=%d, want 2", report.SampleCount)
}
if report.Metrics.LLCMissRate != 0.20 || math.Abs(report.Metrics.LLCOccupancy-0.30) > 0.000001 {
t.Fatalf("unexpected smoothed metrics: %+v", report.Metrics)
}
if report.PSIAvailable {
t.Fatalf("PSIAvailable should use latest sample")
}
if math.Abs(report.InterferenceLevel-0.50) > 0.000001 {
t.Fatalf("interference level=%f, want 0.5", report.InterferenceLevel)
}
if len(report.Detections) != 1 || report.Detections[0].Type != "cache-pollution" {
t.Fatalf("detections=%v", report.Detections)
}
if report.Sequence != 7 || len(report.Availability) != 1 {
t.Fatalf("metadata not preserved: %+v", report)
}
cluster, ok := store.ConsumeDirtyClusterReportWithRuleConfig(RuleConfigMetadata{
Hash: "abc",
LastLoadedTime: base,
})
if !ok {
t.Fatal("expected dirty report")
}
if cluster.RuleConfigHash != "abc" || !cluster.RuleConfigLastLoadedTime.Equal(base) {
t.Fatalf("rule metadata not attached: %+v", cluster)
}
if _, ok := cluster.Nodes["node-b"]; !ok {
t.Fatalf("cluster nodes=%v", cluster.Nodes)
}
if _, ok := store.ConsumeDirtyClusterReport(); ok {
t.Fatal("dirty flag should be cleared after consume")
}
}
func TestNodeStateStoreDropsExpiredReportsAndSorts(t *testing.T) {
store := NewNodeStateStore(10*time.Second, 5*time.Second, testRuleEngine{}, testCalculator{})
old := time.Now().Add(-time.Minute)
now := time.Now()
store.Add("node-z", old, NodeMetrics{LLCMissRate: 0.1}, "ok")
store.Add("node-a", now, NodeMetrics{LLCMissRate: 0.1}, "ok")
cluster := store.ClusterReport()
if _, ok := cluster.Nodes["node-z"]; ok {
t.Fatalf("expired report should be omitted: %v", cluster.Nodes)
}
if _, ok := cluster.Nodes["node-a"]; !ok {
t.Fatalf("fresh report missing: %v", cluster.Nodes)
}
reports := store.Reports()
if len(reports) != 1 || reports[0].NodeName != "node-a" {
t.Fatalf("reports=%v", reports)
}
}
func TestAverageMetricsEmpty(t *testing.T) {
if got := averageMetrics(nil); got != (NodeMetrics{}) {
t.Fatalf("averageMetrics(nil)=%+v", got)
}
}