已合并
add test for TORCH_ACL_INIT_CONFIG_PATH #39953
huangyunlong创建于 7月3日
add test for TORCH_ACL_INIT_CONFIG_PATH #39953
已合并
共 2 个文件变更+145-9
| @@ -1,13 +1,14 @@ | |||
| 1 | +import os | ||
| 1 | import sys | 2 | import sys |
| 2 | import subprocess | 3 | import subprocess |
| 4 | +import unittest | ||
| 5 | +from functools import wraps | ||
| 3 | 6 | ||
| 4 | import torch | 7 | import torch |
| 5 | import torch_npu | 8 | import torch_npu |
| 6 | 9 | ||
| 7 | -import torch_npu.npu.utils as utils | ||
| 8 | - | ||
| 9 | from torch_npu.testing.testcase import TestCase, run_tests | 10 | from torch_npu.testing.testcase import TestCase, run_tests |
| 10 | -from torch_npu.testing.common_utils import SupportedDevices | 11 | +from torch_npu.testing.common_utils import SupportedDevices, SkipIfNotGteCANNVersion |
| 11 | 12 | ||
| 12 | 13 | ||
| 13 | class TestOption(TestCase): | 14 | class TestOption(TestCase): |
| @@ -111,5 +112,134 @@ class TestAclOpInitMode(TestCase): | |||
| 111 | self.assertIn(self.INVALID_VALUE_WARN, stderr) | 112 | self.assertIn(self.INVALID_VALUE_WARN, stderr) |
| 112 | 113 | ||
| 113 | 114 | ||
| 115 | +def _skipIfLazy(fn): | ||
| 116 | + | ||
| 117 | + def wrapper(slf, *args, **kwargs): | ||
| 118 | + if torch_npu.npu.utils._is_gte_cann_version('8.3.RC1'): | ||
| 119 | + raise unittest.SkipTest( | ||
| 120 | + "Test only for non-lazy set_device mode (CANN < 8.3.RC1)") | ||
| 121 | + return fn(slf, *args, **kwargs) | ||
| 122 | + return wrapper | ||
| 123 | + | ||
| 124 | + | ||
| 125 | +class TestAclInitConfigPath(TestCase): | ||
| 126 | + | ||
| 127 | + LACKS_DEFAULT_DEVICE_MSG = "lacks 'defaultDevice'" | ||
| 128 | + HAS_DEFAULT_DEVICE_MSG = "contains 'defaultDevice'" | ||
| 129 | + PARSE_FAILED_MSG = "Failed to parse user acl json" | ||
| 130 | + INVALID_PATH_MSG = "is invalid" | ||
| 131 | + OPEN_FAILED_MSG = "Failed to open user acl json" | ||
| 132 | + | ||
| 133 | + def _make_temp_json(self, content): | ||
| 134 | + import tempfile | ||
| 135 | + tmp = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) | ||
| 136 | + tmp.write(content) | ||
| 137 | + tmp.close() | ||
| 138 | + self.addCleanup(os.unlink, tmp.name) | ||
| 139 | + return tmp.name | ||
| 140 | + | ||
| 141 | + def _run_with_env(self, json_path): | ||
| 142 | + test_script = ( | ||
| 143 | + f"import os; " | ||
| 144 | + f"os.environ['TORCH_ACL_INIT_CONFIG_PATH']='{json_path}'; " | ||
| 145 | + f"import torch; import torch_npu; torch_npu.npu.set_device(0)" | ||
| 146 | + ) | ||
| 147 | + result = subprocess.run( | ||
| 148 | + [sys.executable, '-c', test_script], | ||
| 149 | + capture_output=True, text=True | ||
| 150 | + ) | ||
| 151 | + return result | ||
| 152 | + | ||
| 153 | + # ---- 设备无关:路径无效/解析失败 ---- | ||
| 154 | + | ||
| 155 | + def test_invalid_json(self): | ||
| 156 | + """非法 JSON 格式应抛 RuntimeError""" | ||
| 157 | + json_path = self._make_temp_json('not a json{{{') | ||
| 158 | + ret = self._run_with_env(json_path) | ||
| 159 | + self.assertNotEqual(ret.returncode, 0) | ||
| 160 | + self.assertIn(self.PARSE_FAILED_MSG, ret.stderr, | ||
| 161 | + "Invalid JSON should raise RuntimeError") | ||
| 162 | + | ||
| 163 | + def test_nonexistent_path(self): | ||
| 164 | + """不存在的路径应抛 RuntimeError""" | ||
| 165 | + json_path = '/tmp/nonexistent_acl_config_for_test.json' | ||
| 166 | + ret = self._run_with_env(json_path) | ||
| 167 | + self.assertNotEqual(ret.returncode, 0) | ||
| 168 | + self.assertIn(self.INVALID_PATH_MSG, ret.stderr, | ||
| 169 | + "Non-existent path should raise RuntimeError") | ||
| 170 | + | ||
| 171 | + def test_empty_json_file(self): | ||
| 172 | + """空 JSON 文件应抛 RuntimeError""" | ||
| 173 | + json_path = self._make_temp_json('') | ||
| 174 | + ret = self._run_with_env(json_path) | ||
| 175 | + self.assertNotEqual(ret.returncode, 0) | ||
| 176 | + self.assertIn(self.PARSE_FAILED_MSG, ret.stderr, | ||
| 177 | + "Empty JSON file should raise RuntimeError") | ||
| 178 | + | ||
| 179 | + # ---- non-lazy 模式(CANN < 8.3.RC1) ---- | ||
| 180 | + | ||
| 181 | + | ||
| 182 | + def test_valid_json_non_lazy(self): | ||
| 183 | + """non-lazy 模式:不含 defaultDevice 的用户 JSON 正常使用""" | ||
| 184 | + json_path = self._make_temp_json('{"dump":{"dump_scene":"lite_exception"}}') | ||
| 185 | + ret = self._run_with_env(json_path) | ||
| 186 | + self.assertEqual(ret.returncode, 0, | ||
| 187 | + f"Valid JSON in non-lazy should succeed, got: {ret.stderr}") | ||
| 188 | + | ||
| 189 | + | ||
| 190 | + def test_valid_json_with_default_device_in_non_lazy(self): | ||
| 191 | + """non-lazy 模式:含 defaultDevice 应抛 RuntimeError""" | ||
| 192 | + json_path = self._make_temp_json( | ||
| 193 | + '{"dump":{"dump_scene":"lite_exception"},"defaultDevice":{"default_device":"0"}}' | ||
| 194 | + ) | ||
| 195 | + ret = self._run_with_env(json_path) | ||
| 196 | + self.assertNotEqual(ret.returncode, 0) | ||
| 197 | + self.assertIn(self.HAS_DEFAULT_DEVICE_MSG, ret.stderr, | ||
| 198 | + "Non-lazy mode with defaultDevice should raise RuntimeError") | ||
| 199 | + | ||
| 200 | + # ---- lazy 模式(CANN >= 8.3.RC1) ---- | ||
| 201 | + | ||
| 202 | + | ||
| 203 | + def test_valid_json_with_default_device_in_lazy(self): | ||
| 204 | + """lazy 模式:含合法 defaultDevice 正常使用""" | ||
| 205 | + json_path = self._make_temp_json( | ||
| 206 | + '{"dump":{"dump_scene":"lite_exception"},"defaultDevice":{"default_device":"0"}}' | ||
| 207 | + ) | ||
| 208 | + ret = self._run_with_env(json_path) | ||
| 209 | + self.assertEqual(ret.returncode, 0, | ||
| 210 | + f"Valid JSON in lazy should succeed, got: {ret.stderr}") | ||
| 211 | + | ||
| 212 | + | ||
| 213 | + def test_json_without_default_device_in_lazy(self): | ||
| 214 | + """lazy 模式:不含 defaultDevice 应抛 RuntimeError""" | ||
| 215 | + json_path = self._make_temp_json('{"dump":{"dump_scene":"lite_exception"}}') | ||
| 216 | + ret = self._run_with_env(json_path) | ||
| 217 | + self.assertNotEqual(ret.returncode, 0) | ||
| 218 | + self.assertIn(self.LACKS_DEFAULT_DEVICE_MSG, ret.stderr, | ||
| 219 | + "Lazy mode without defaultDevice should raise RuntimeError") | ||
| 220 | + | ||
| 221 | + | ||
| 222 | + def test_json_wrong_default_device_value_in_lazy(self): | ||
| 223 | + """lazy 模式:default_device 值不为 '0' 应抛 RuntimeError""" | ||
| 224 | + json_path = self._make_temp_json( | ||
| 225 | + '{"defaultDevice":{"default_device":"1"}}' | ||
| 226 | + ) | ||
| 227 | + ret = self._run_with_env(json_path) | ||
| 228 | + self.assertNotEqual(ret.returncode, 0) | ||
| 229 | + self.assertIn('requires', ret.stderr, | ||
| 230 | + "default_device='1' should raise RuntimeError") | ||
| 231 | + | ||
| 232 | + | ||
| 233 | + def test_json_default_device_wrong_type(self): | ||
| 234 | + """lazy 模式:defaultDevice 非 object 应抛 RuntimeError""" | ||
| 235 | + json_path = self._make_temp_json( | ||
| 236 | + '{"defaultDevice":"not_an_object","dump":{"dump_scene":"lite_exception"}}' | ||
| 237 | + ) | ||
| 238 | + ret = self._run_with_env(json_path) | ||
| 239 | + self.assertNotEqual(ret.returncode, 0) | ||
| 240 | + self.assertIn(self.LACKS_DEFAULT_DEVICE_MSG, ret.stderr, | ||
| 241 | + "defaultDevice as string should raise RuntimeError") | ||
| 242 | + | ||
| 243 | + | ||
| 114 | if __name__ == "__main__": | 244 | if __name__ == "__main__": |
| 115 | run_tests() | 245 | run_tests() |
| @@ -116,13 +116,15 @@ std::string GetAclConfigJsonPath() | |||
| 116 | std::string json_path = std::string(acl_init_path); | 116 | std::string json_path = std::string(acl_init_path); |
| 117 | std::string json_path_str = torch_npu::toolkit::profiler::Utils::RealPath(json_path); | 117 | std::string json_path_str = torch_npu::toolkit::profiler::Utils::RealPath(json_path); |
| 118 | if (json_path_str.empty()) { | 118 | if (json_path_str.empty()) { |
| 119 | - TORCH_CHECK(false, "TORCH_ACL_INIT_CONFIG_PATH ", acl_init_path, | 119 | + TORCH_CHECK(false, "TORCH_ACL_INIT_CONFIG_PATH ", acl_init_path, " is invalid. ", |
| 120 | - " is invalid.", PTA_ERROR(ErrCode::UNAVAIL)); | 120 | + "Please set the environment variable 'TORCH_ACL_INIT_CONFIG_PATH' to a valid JSON config file path.", |
| 121 | + PTA_ERROR(ErrCode::UNAVAIL)); | ||
| 121 | } | 122 | } |
| 122 | 123 | ||
| 123 | std::ifstream config_file(json_path_str); | 124 | std::ifstream config_file(json_path_str); |
| 124 | if (!config_file.is_open()) { | 125 | if (!config_file.is_open()) { |
| 125 | TORCH_CHECK(false, "Failed to open user acl json ", json_path_str, | 126 | TORCH_CHECK(false, "Failed to open user acl json ", json_path_str, |
| 127 | + ". Please ensure the file exists and has read permission.", | ||
| 126 | PTA_ERROR(ErrCode::UNAVAIL)); | 128 | PTA_ERROR(ErrCode::UNAVAIL)); |
| 127 | } | 129 | } |
| 128 | 130 | ||
| @@ -132,13 +134,15 @@ std::string GetAclConfigJsonPath() | |||
| 132 | config_file >> config; | 134 | config_file >> config; |
| 133 | } catch (const std::exception& e) { | 135 | } catch (const std::exception& e) { |
| 134 | TORCH_CHECK(false, "Failed to parse user acl json ", json_path_str, | 136 | TORCH_CHECK(false, "Failed to parse user acl json ", json_path_str, |
| 135 | - " : ", e.what(), PTA_ERROR(ErrCode::UNAVAIL)); | 137 | + ". Please check that the file contains valid JSON syntax. Error: ", e.what(), |
| 138 | + PTA_ERROR(ErrCode::UNAVAIL)); | ||
| 136 | } | 139 | } |
| 137 | 140 | ||
| 138 | if (c10_npu::is_lazy_set_device()) { | 141 | if (c10_npu::is_lazy_set_device()) { |
| 139 | if (!config.contains("defaultDevice") || !config["defaultDevice"].is_object()) { | 142 | if (!config.contains("defaultDevice") || !config["defaultDevice"].is_object()) { |
| 140 | TORCH_CHECK(false, "User acl json ", json_path_str, | 143 | TORCH_CHECK(false, "User acl json ", json_path_str, |
| 141 | - " lacks 'defaultDevice' object, required for lazy set_device mode.", | 144 | + " lacks 'defaultDevice' object, required for lazy set_device mode. ", |
| 145 | + "Please add: \"defaultDevice\": {\"default_device\": \"0\"}", | ||
| 142 | PTA_ERROR(ErrCode::VALUE)); | 146 | PTA_ERROR(ErrCode::VALUE)); |
| 143 | } | 147 | } |
| 144 | const auto& default_dev = config["defaultDevice"]; | 148 | const auto& default_dev = config["defaultDevice"]; |
| @@ -146,13 +150,15 @@ std::string GetAclConfigJsonPath() | |||
| 146 | !default_dev["default_device"].is_string() || | 150 | !default_dev["default_device"].is_string() || |
| 147 | default_dev["default_device"].get<std::string>() != "0") { | 151 | default_dev["default_device"].get<std::string>() != "0") { |
| 148 | TORCH_CHECK(false, "User acl json ", json_path_str, | 152 | TORCH_CHECK(false, "User acl json ", json_path_str, |
| 149 | - " requires 'defaultDevice.default_device' to be \"0\" in lazy set_device mode.", | 153 | + " requires 'defaultDevice.default_device' to be \"0\" in lazy set_device mode. ", |
| 154 | + "Please set: \"defaultDevice\": {\"default_device\": \"0\"}", | ||
| 150 | PTA_ERROR(ErrCode::VALUE)); | 155 | PTA_ERROR(ErrCode::VALUE)); |
| 151 | } | 156 | } |
| 152 | } else { | 157 | } else { |
| 153 | if (config.contains("defaultDevice")) { | 158 | if (config.contains("defaultDevice")) { |
| 154 | TORCH_CHECK(false, "User acl json ", json_path_str, | 159 | TORCH_CHECK(false, "User acl json ", json_path_str, |
| 155 | - " contains 'defaultDevice' which is not expected in non-lazy set_device mode.", | 160 | + " contains 'defaultDevice' which is not expected in non-lazy set_device mode. ", |
| 161 | + "Please remove the 'defaultDevice' field from your JSON config.", | ||
| 156 | PTA_ERROR(ErrCode::VALUE)); | 162 | PTA_ERROR(ErrCode::VALUE)); |
| 157 | } | 163 | } |
| 158 | } | 164 | } |