From a8a974848f2d9d82f5dd6ec89c2730307bc50d9b Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Thu, 9 Jul 2026 16:56:16 -0700 Subject: [PATCH 1/6] Fix flaky storage tests --- .../tests/conftest.py | 1 - .../tests/test_storage.py | 29 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/conftest.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/conftest.py index 8ef165d56077..63a4c52f1d8d 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/conftest.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/conftest.py @@ -31,4 +31,3 @@ if sys.version_info < (3, 6): collect_ignore_glob.append("*") -collect_ignore = ["test_storage.py"] diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py index 135d02d8213e..4856f6bede16 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py @@ -39,7 +39,7 @@ def func(*_args, **_kwargs): def clean_folder(folder): - if os.path.isfile(folder): + if os.path.isdir(folder): for filename in os.listdir(folder): file_path = os.path.join(folder, filename) try: @@ -51,6 +51,17 @@ def clean_folder(folder): print("Failed to delete %s. Reason: %s" % (file_path, e)) +def reset_local_storage_setup_state(): + from azure.monitor.opentelemetry.exporter.statsbeat.customer._state import ( + _LOCAL_STORAGE_SETUP_STATE, + _LOCAL_STORAGE_SETUP_STATE_LOCK, + ) + + with _LOCAL_STORAGE_SETUP_STATE_LOCK: + _LOCAL_STORAGE_SETUP_STATE["READONLY"] = False + _LOCAL_STORAGE_SETUP_STATE["EXCEPTION_OCCURRED"] = "" + + # pylint: disable=unused-variable class TestLocalFileBlob(unittest.TestCase): @classmethod @@ -61,6 +72,10 @@ def setup_class(cls): def tearDownClass(cls): shutil.rmtree(TEST_FOLDER, True) + def setUp(self): + clean_folder(TEST_FOLDER) + os.makedirs(TEST_FOLDER, exist_ok=True) + def tearDown(self): clean_folder(TEST_FOLDER) @@ -324,6 +339,18 @@ class TestLocalFileStorage(unittest.TestCase): def tearDownClass(cls): shutil.rmtree(TEST_FOLDER, True) + def setUp(self): + reset_local_storage_setup_state() + clean_folder(TEST_FOLDER) + os.makedirs(TEST_FOLDER, exist_ok=True) + periodic_task_patcher = mock.patch(f"{STORAGE_MODULE}.PeriodicTask") + self._periodic_task_mock = periodic_task_patcher.start() + self.addCleanup(periodic_task_patcher.stop) + + def tearDown(self): + reset_local_storage_setup_state() + clean_folder(TEST_FOLDER) + def test_get_nothing(self): with LocalFileStorage(os.path.join(TEST_FOLDER, "test", "a")) as stor: pass From 4e43cfbf84642786eaf7142a395c6d95526f4d12 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Fri, 10 Jul 2026 09:22:17 -0700 Subject: [PATCH 2/6] Fix tests --- .../tests/test_storage.py | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py index 4856f6bede16..618793788c83 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py @@ -4,6 +4,7 @@ import errno import os import shutil +import tempfile import unittest from unittest import mock @@ -51,17 +52,6 @@ def clean_folder(folder): print("Failed to delete %s. Reason: %s" % (file_path, e)) -def reset_local_storage_setup_state(): - from azure.monitor.opentelemetry.exporter.statsbeat.customer._state import ( - _LOCAL_STORAGE_SETUP_STATE, - _LOCAL_STORAGE_SETUP_STATE_LOCK, - ) - - with _LOCAL_STORAGE_SETUP_STATE_LOCK: - _LOCAL_STORAGE_SETUP_STATE["READONLY"] = False - _LOCAL_STORAGE_SETUP_STATE["EXCEPTION_OCCURRED"] = "" - - # pylint: disable=unused-variable class TestLocalFileBlob(unittest.TestCase): @classmethod @@ -340,15 +330,10 @@ def tearDownClass(cls): shutil.rmtree(TEST_FOLDER, True) def setUp(self): - reset_local_storage_setup_state() clean_folder(TEST_FOLDER) os.makedirs(TEST_FOLDER, exist_ok=True) - periodic_task_patcher = mock.patch(f"{STORAGE_MODULE}.PeriodicTask") - self._periodic_task_mock = periodic_task_patcher.start() - self.addCleanup(periodic_task_patcher.stop) def tearDown(self): - reset_local_storage_setup_state() clean_folder(TEST_FOLDER) def test_get_nothing(self): @@ -397,11 +382,19 @@ def test_put_max_size(self): def test_check_storage_size_full(self): test_input = (1, 2, 3) - test_path = os.path.join(TEST_FOLDER, "asd2") - os.makedirs(test_path, exist_ok=True) + # Dedicated temp directory so no other test/worker shares this path. + test_path = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, test_path, ignore_errors=True) with mock.patch.object(LocalFileStorage, "_check_and_set_folder_permissions", return_value=True): with LocalFileStorage(test_path, 1) as stor: - stor.put(test_input) + # Stop the background maintenance thread so it cannot rename or + # remove the blob file while _check_storage_size() walks the + # folder (which would surface as an OSError -> uncounted file -> + # _check_storage_size() incorrectly returning True). + stor._maintenance_task.cancel() + # Use lease_period=0 so a stable ".blob" file is written that + # nothing in the background will touch. + stor.put(test_input, lease_period=0) self.assertFalse(stor._check_storage_size()) def test_check_storage_size_not_full(self): @@ -494,7 +487,13 @@ def test_put_persistence_capacity_reached(self): def test_put_success_returns_localfileblob(self): test_input = (1, 2, 3) - with LocalFileStorage(os.path.join(TEST_FOLDER, "success_test")) as stor: + # Dedicated temp directory so no other test/worker shares this path. + test_path = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, test_path, ignore_errors=True) + with LocalFileStorage(test_path) as stor: + # Stop the background maintenance thread so it cannot remove the + # blob before it is read back. + stor._maintenance_task.cancel() result = stor.put(test_input, lease_period=0) # No lease period so file is immediately available self.assertIsInstance(result, StorageExportResult) self.assertEqual(stor.get().get(), test_input) @@ -553,11 +552,15 @@ def test_put_with_lease_period(self): def test_put_default_lease_period(self): test_input = (1, 2, 3) - test_path = os.path.join(TEST_FOLDER, "default_lease_test") - os.makedirs(test_path, exist_ok=True) + # Dedicated temp directory so no other test/worker shares this path. + test_path = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, test_path, ignore_errors=True) with mock.patch.object(LocalFileStorage, "_check_and_set_folder_permissions", return_value=True): with LocalFileStorage(test_path, lease_period=90) as stor: + # Stop the background maintenance thread so it cannot interfere + # with the blob written below. + stor._maintenance_task.cancel() result = stor.put(test_input) self.assertIsInstance(result, StorageExportResult) # File should be created with lease (since default lease_period > 0) From 53ba0285cb67b043bde2fec7c747fe1e3d586f31 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Fri, 10 Jul 2026 09:43:32 -0700 Subject: [PATCH 3/6] Fix tests --- .../tests/test_storage.py | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py index 618793788c83..4778be341dae 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py @@ -54,20 +54,12 @@ def clean_folder(folder): # pylint: disable=unused-variable class TestLocalFileBlob(unittest.TestCase): - @classmethod - def setup_class(cls): - os.makedirs(TEST_FOLDER, exist_ok=True) - - @classmethod - def tearDownClass(cls): - shutil.rmtree(TEST_FOLDER, True) - def setUp(self): - clean_folder(TEST_FOLDER) - os.makedirs(TEST_FOLDER, exist_ok=True) + global TEST_FOLDER + TEST_FOLDER = tempfile.mkdtemp() def tearDown(self): - clean_folder(TEST_FOLDER) + shutil.rmtree(TEST_FOLDER, ignore_errors=True) def test_delete(self): blob = LocalFileBlob(os.path.join(TEST_FOLDER, "foobar")) @@ -325,16 +317,12 @@ def test_lease_with_existing_lock(self): # pylint: disable=protected-access, too-many-public-methods class TestLocalFileStorage(unittest.TestCase): - @classmethod - def tearDownClass(cls): - shutil.rmtree(TEST_FOLDER, True) - def setUp(self): - clean_folder(TEST_FOLDER) - os.makedirs(TEST_FOLDER, exist_ok=True) + global TEST_FOLDER + TEST_FOLDER = tempfile.mkdtemp() def tearDown(self): - clean_folder(TEST_FOLDER) + shutil.rmtree(TEST_FOLDER, ignore_errors=True) def test_get_nothing(self): with LocalFileStorage(os.path.join(TEST_FOLDER, "test", "a")) as stor: From 5b0e4dbe2997fa05b0a1fd9850a1bf2be57b9b78 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Fri, 10 Jul 2026 10:14:03 -0700 Subject: [PATCH 4/6] Retrigger CI/CD pipeline From 46421bd32772ec1539e313b9f0dfdba9af9aac55 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Fri, 10 Jul 2026 10:30:55 -0700 Subject: [PATCH 5/6] Remove extra guard --- .../tests/test_storage.py | 28 ++++--------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py index 4778be341dae..025dfe02c919 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/test_storage.py @@ -370,18 +370,10 @@ def test_put_max_size(self): def test_check_storage_size_full(self): test_input = (1, 2, 3) - # Dedicated temp directory so no other test/worker shares this path. - test_path = tempfile.mkdtemp() - self.addCleanup(shutil.rmtree, test_path, ignore_errors=True) + test_path = os.path.join(TEST_FOLDER, "asd2") + os.makedirs(test_path, exist_ok=True) with mock.patch.object(LocalFileStorage, "_check_and_set_folder_permissions", return_value=True): with LocalFileStorage(test_path, 1) as stor: - # Stop the background maintenance thread so it cannot rename or - # remove the blob file while _check_storage_size() walks the - # folder (which would surface as an OSError -> uncounted file -> - # _check_storage_size() incorrectly returning True). - stor._maintenance_task.cancel() - # Use lease_period=0 so a stable ".blob" file is written that - # nothing in the background will touch. stor.put(test_input, lease_period=0) self.assertFalse(stor._check_storage_size()) @@ -475,13 +467,7 @@ def test_put_persistence_capacity_reached(self): def test_put_success_returns_localfileblob(self): test_input = (1, 2, 3) - # Dedicated temp directory so no other test/worker shares this path. - test_path = tempfile.mkdtemp() - self.addCleanup(shutil.rmtree, test_path, ignore_errors=True) - with LocalFileStorage(test_path) as stor: - # Stop the background maintenance thread so it cannot remove the - # blob before it is read back. - stor._maintenance_task.cancel() + with LocalFileStorage(os.path.join(TEST_FOLDER, "success_test")) as stor: result = stor.put(test_input, lease_period=0) # No lease period so file is immediately available self.assertIsInstance(result, StorageExportResult) self.assertEqual(stor.get().get(), test_input) @@ -540,15 +526,11 @@ def test_put_with_lease_period(self): def test_put_default_lease_period(self): test_input = (1, 2, 3) - # Dedicated temp directory so no other test/worker shares this path. - test_path = tempfile.mkdtemp() - self.addCleanup(shutil.rmtree, test_path, ignore_errors=True) + test_path = os.path.join(TEST_FOLDER, "default_lease_test") + os.makedirs(test_path, exist_ok=True) with mock.patch.object(LocalFileStorage, "_check_and_set_folder_permissions", return_value=True): with LocalFileStorage(test_path, lease_period=90) as stor: - # Stop the background maintenance thread so it cannot interfere - # with the blob written below. - stor._maintenance_task.cancel() result = stor.put(test_input) self.assertIsInstance(result, StorageExportResult) # File should be created with lease (since default lease_period > 0) From f97b9cd8a7f6e812c4124d9a89599b20e39a926f Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Fri, 10 Jul 2026 11:56:45 -0700 Subject: [PATCH 6/6] Fix format --- .../azure-monitor-opentelemetry-exporter/tests/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/conftest.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/conftest.py index 63a4c52f1d8d..0a233bc4d432 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/conftest.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/conftest.py @@ -30,4 +30,3 @@ collect_ignore_glob = [] if sys.version_info < (3, 6): collect_ignore_glob.append("*") -