fault-log-analyzer/tests/test_cluster.py

93 lines
2.9 KiB
Python

"""TF-IDF 特征 + DBSCAN 聚类测试。"""
import _bootstrap # noqa: F401
import unittest
from fault_log_analyzer.cluster import (
Clusterer,
cosine_distance,
cosine_similarity,
dbscan,
tfidf_vectors,
)
class TestTfidf(unittest.TestCase):
def test_empty(self):
self.assertEqual(tfidf_vectors([]), [])
def test_vectors_shape(self):
vecs = tfidf_vectors(["no space left on device", "out of memory error"])
self.assertEqual(len(vecs), 2)
def test_identical_docs_similar(self):
vecs = tfidf_vectors(["disk full", "disk full"])
self.assertAlmostEqual(cosine_similarity(vecs[0], vecs[1]), 1.0)
def test_distinct_docs_less_similar(self):
vecs = tfidf_vectors(["disk full error", "memory exhausted"])
self.assertLess(cosine_similarity(vecs[0], vecs[1]), 1.0)
class TestCosine(unittest.TestCase):
def test_similarity_zero_for_empty(self):
self.assertEqual(cosine_similarity({}, {}), 0.0)
def test_distance_identical_is_zero(self):
self.assertAlmostEqual(cosine_distance({"a": 1.0}, {"a": 1.0}), 0.0)
def test_distance_is_one_for_orthogonal(self):
self.assertAlmostEqual(cosine_distance({"a": 1.0}, {"b": 1.0}), 1.0)
class TestDbscan(unittest.TestCase):
def test_empty(self):
self.assertEqual(dbscan([], 0.75, 5), [])
def test_single_sample_is_noise(self):
self.assertEqual(dbscan([{"a": 1.0}], 0.75, 5), [-1])
def test_two_identical_with_min2(self):
labels = dbscan([{"a": 1.0}, {"a": 1.0}], 0.75, 2)
self.assertEqual(labels, [0, 0])
def test_two_distinct_with_min2_are_noise(self):
labels = dbscan([{"a": 1.0}, {"b": 1.0}], 0.75, 2)
self.assertEqual(labels, [-1, -1])
class TestClusterer(unittest.TestCase):
def test_fit_predict_small_returns_noise(self):
c = Clusterer(eps=0.75, min_samples=5)
self.assertEqual(c.fit_predict(["only one message"]), [-1])
def test_assign_empty_centroids(self):
self.assertEqual(Clusterer().assign("anything"), -1)
def test_fit_predict_group(self):
messages = [
"no space left on device",
"no space left on device /var",
"no space left on device /tmp",
"out of memory error",
"out of memory killed process",
]
c = Clusterer(eps=0.75, min_samples=2)
labels = c.fit_predict(messages)
self.assertEqual(len(labels), 5)
self.assertGreaterEqual(c.n_clusters, 1)
def test_assign_to_nearest_cluster(self):
messages = [
"no space left on device",
"no space left on device /var",
"no space left on device /tmp",
]
c = Clusterer(eps=0.75, min_samples=2)
c.fit_predict(messages)
self.assertGreaterEqual(c.n_clusters, 1)
self.assertGreaterEqual(c.assign("no space left on device /opt"), 0)
if __name__ == "__main__":
unittest.main()