1# Copyright (c) 2017 Ultimaker B.V.
2# Uranium is released under the terms of the LGPLv3 or higher.
3
4import os
5import platform
6import unittest
7from unittest import TestCase
8from unittest.mock import patch, PropertyMock
9import tempfile
10import pytest
11
12from UM.Resources import Resources, ResourceTypeError, UnsupportedStorageTypeError
13
14
15class TestResources(TestCase):
16
17    #
18    # getConfigStorageRootPath() tests
19    #
20    @unittest.skipIf(platform.system() != "Windows", "Not on Windows")
21    def test_getConfigStorageRootPath_Windows(self):
22        config_root_path = Resources._getConfigStorageRootPath()
23        expected_config_root_path = os.getenv("APPDATA")
24        self.assertEqual(expected_config_root_path, config_root_path,
25                         "expected %s, got %s" % (expected_config_root_path, config_root_path))
26
27    @unittest.skipIf(platform.system() != "Linux", "Not on linux")
28    def test_getConfigStorageRootPath_Linux(self):
29        # no XDG_CONFIG_HOME defined
30        if "XDG_CONFIG_HOME" in os.environ:
31            del os.environ["XDG_CONFIG_HOME"]
32        config_root_path = Resources._getConfigStorageRootPath()
33        expected_config_root_path = os.path.expanduser("~/.config")
34        self.assertEqual(expected_config_root_path, config_root_path,
35                         "expected %s, got %s" % (expected_config_root_path, config_root_path))
36
37        # XDG_CONFIG_HOME defined
38        os.environ["XDG_CONFIG_HOME"] = "/tmp"
39        config_root_path = Resources._getConfigStorageRootPath()
40        expected_config_root_path = "/tmp"
41        self.assertEqual(expected_config_root_path, config_root_path,
42                         "expected %s, got %s" % (expected_config_root_path, config_root_path))
43
44    @unittest.skipIf(platform.system() != "Darwin", "Not on mac")
45    def test_getConfigStorageRootPath_Mac(self):
46        config_root_path = Resources._getConfigStorageRootPath()
47        expected_config_root_path = os.path.expanduser("~/Library/Application Support")
48        self.assertEqual(expected_config_root_path, config_root_path,
49                         "expected %s, got %s" % (expected_config_root_path, config_root_path))
50
51    @unittest.skipIf(platform.system() != "Windows", "Not on Windows")
52    def test_getDataStorageRootPath_Windows(self):
53        data_root_path = Resources._getDataStorageRootPath()
54        self.assertIsNone(data_root_path, "expected None, got %s" % data_root_path)
55
56    @unittest.skipIf(platform.system() != "Linux", "Not on linux")
57    def test_getDataStorageRootPath_Linux(self):
58        # no XDG_CONFIG_HOME defined
59        if "XDG_DATA_HOME" in os.environ:
60            del os.environ["XDG_DATA_HOME"]
61        data_root_path = Resources._getDataStorageRootPath()
62        expected_data_root_path = os.path.expanduser("~/.local/share")
63        self.assertEqual(expected_data_root_path, data_root_path,
64                         "expected %s, got %s" % (expected_data_root_path, data_root_path))
65
66        # XDG_CONFIG_HOME defined
67        os.environ["XDG_DATA_HOME"] = "/tmp"
68        data_root_path = Resources._getDataStorageRootPath()
69        expected_data_root_path = "/tmp"
70        self.assertEqual(expected_data_root_path, data_root_path,
71                         "expected %s, got %s" % (expected_data_root_path, data_root_path))
72
73    @unittest.skipIf(platform.system() != "Darwin", "Not on mac")
74    def test_getDataStorageRootPath_Mac(self):
75        data_root_path = Resources._getDataStorageRootPath()
76        self.assertIsNone(data_root_path, "expected None, got %s" % data_root_path)
77
78    @unittest.skipIf(platform.system() != "Windows", "Not on Windows")
79    def test_getCacheStorageRootPath_Windows(self):
80        if platform.system() != "Windows":
81            self.skipTest("not on Windows")
82
83        cache_root_path = Resources._getCacheStorageRootPath()
84        expected_cache_root_path = os.getenv("LOCALAPPDATA")
85        self.assertEqual(expected_cache_root_path, cache_root_path,
86                         "expected %s, got %s" % (expected_cache_root_path, cache_root_path))
87
88    @unittest.skipIf(platform.system() != "Linux", "Not on linux")
89    def test_getCacheStorageRootPath_Linux(self):
90        cache_root_path = Resources._getCacheStorageRootPath()
91        expected_cache_root_path = os.path.expanduser("~/.cache")
92        self.assertEqual(expected_cache_root_path, cache_root_path,
93                         "expected %s, got %s" % (expected_cache_root_path, cache_root_path))
94
95    @unittest.skipIf(platform.system() != "Darwin", "Not on mac")
96    def test_getCacheStorageRootPath_Mac(self):
97        cache_root_path = Resources._getCacheStorageRootPath()
98        self.assertIsNone("expected None, got %s" % cache_root_path)
99
100    @unittest.skipIf(platform.system() != "Linux", "Not on linux")
101    def test_getPossibleConfigStorageRootPathList_Linux(self):
102        # We didn't add any paths, so it will use defaults
103        assert Resources._getPossibleConfigStorageRootPathList() == ['/tmp/test']
104
105    @unittest.skipIf(platform.system() != "Linux", "Not on linux")
106    def test_getPossibleDataStorageRootPathList_Linux(self):
107        # We didn't add any paths, so it will use defaults
108        assert Resources._getPossibleDataStorageRootPathList() == ['/tmp/test']
109
110    def test_factoryReset(self):
111        # FIXME: This is a temporary workaround. A proper fix should be to make the home directory configurable so a
112        #        unique temporary directory can be used for each test and it can removed afterwards.
113        # HACK: Record the number of files and directories in the data storage directory before the factory reset,
114        # so after the reset, we can compare if there's a new ZIP file being created. Note that this will not always
115        # work, especially when there are multiple tests running on the same host at the same time.
116        original_filenames = os.listdir(os.path.dirname(Resources.getDataStoragePath()))
117
118        Resources.factoryReset()
119        # Check if the data is deleted!
120        assert len(os.listdir(Resources.getDataStoragePath())) == 0
121
122        # The data folder should still be there, but it should also have created a zip with the data it deleted.
123        new_filenames = os.listdir(os.path.dirname(Resources.getDataStoragePath()))
124        assert len(new_filenames) - len(original_filenames) == 1
125
126        # Clean up after our ass.
127        folder = os.path.dirname(Resources.getDataStoragePath())
128        for file in os.listdir(folder):
129            file_path = os.path.join(folder, file)
130            try:
131                os.unlink(file_path)
132            except:
133                pass
134        folder = os.path.dirname(Resources.getDataStoragePath())
135        for file in os.listdir(folder):
136            file_path = os.path.join(folder, file)
137            try:
138                os.unlink(file_path)
139            except:
140                pass
141
142    def test_copyLatestDirsIfPresent(self):
143        # Just don't fail.
144        Resources._copyLatestDirsIfPresent()
145
146    @unittest.skipIf(platform.system() != "Linux", "Not on linux")
147    def test_getStoragePathForType_Linux(self):
148        with pytest.raises(ResourceTypeError):
149            # No types have been added, so this should break!
150            Resources.getAllResourcesOfType(0)
151        with pytest.raises(UnsupportedStorageTypeError):
152            # We still haven't added it, so it should fail (again)
153            Resources.getStoragePathForType(0)
154
155        Resources.addStorageType(0, "/test")
156        assert Resources.getStoragePathForType(0) == "/test"
157
158    def test_getAllResourcesOfType(self):
159        resouce_folder = tempfile.mkdtemp("test_folder_origin")
160        resource_file = tempfile.mkstemp(dir=str(resouce_folder))
161        Resources.addStorageType(111, resouce_folder)
162        assert Resources.getAllResourcesOfType(111) == [resource_file[1]]
163
164    def test_copyVersionFolder(self):
165
166        import os
167        folder_to_copy = tempfile.mkdtemp("test_folder_origin")
168        file_to_copy = tempfile.mkstemp(dir=str(folder_to_copy))
169
170        folder_to_move_to = tempfile.mkdtemp("test_folder_destination")
171
172        Resources.copyVersionFolder(str(folder_to_copy), str(folder_to_move_to) + "/target")
173        # We put a temp file in the folder to copy, check if it arrived there.
174        assert len(os.listdir(str(folder_to_move_to) + "/target")) == 1
175
176    # The app version is "dev", and this is considered as the latest version possible. It will upgrade from the highest
177    # "<major>.<minor>" directory that's available.
178    def test_findLatestDirInPathsDevAppVersion(self):
179        test_folder = tempfile.mkdtemp("test_folder")
180        for folder in ("whatever", "2.1", "3.4", "4.2", "5.1", "10.2", "50.7"):
181            os.mkdir(os.path.join(test_folder, folder))
182
183        with patch("UM.Resources.Resources.ApplicationVersion", new_callable = PropertyMock(return_value = "dev")):
184            # We should obviously find the folder that was created by means of the ApplicationVersion.
185            assert Resources._findLatestDirInPaths([test_folder]) == os.path.join(test_folder, "50.7")
186
187    # Tests _findLatestDirInPaths() with a normal application version, that is a version like "<major>.<minor>".
188    # In this case, it should get the highest available "<major>.<minor>" directory but no higher than the application
189    # version itself.
190    def test_findLatestDirInPathsNormalAppVersion(self):
191        test_folder = tempfile.mkdtemp("test_folder")
192        for folder in ("whatever", "2.1", "3.4", "4.2", "5.1", "10.2", "50.7"):
193            os.mkdir(os.path.join(test_folder, folder))
194
195        with patch("UM.Resources.Resources.ApplicationVersion", new_callable = PropertyMock(return_value = "4.3")):
196            # We should obviously find the folder that was created by means of the ApplicationVersion.
197            assert Resources._findLatestDirInPaths([test_folder]) == os.path.join(test_folder, "4.2")
198
199    # In this case, our app version is 4.3, but all the available directories do not have names "<major>.<minor>".
200    # _findLatestDirInPaths() should return None.
201    def test_findLatestDirInPathsNormalAppVersionNoValidUpgrade(self):
202        test_folder = tempfile.mkdtemp("test_folder")
203        for folder in ("whatever1", "whatever2", "foobar1", "dev", "master", "test"):
204            os.mkdir(os.path.join(test_folder, folder))
205
206        with patch("UM.Resources.Resources.ApplicationVersion", new_callable = PropertyMock(return_value = "4.3")):
207            # There is no folder that matches what we're looking for!
208            assert Resources._findLatestDirInPaths([test_folder]) is None
209
210    # In this case, our app version is 4.3, but all there's no available directory named as "<major>.<minor>".
211    # _findLatestDirInPaths() should return None.
212    def test_findLatestDirInPathsNormalAppVersionEmptySearchFolder(self):
213        test_folder = tempfile.mkdtemp("test_folder")
214
215        with patch("UM.Resources.Resources.ApplicationVersion", new_callable = PropertyMock(return_value = "4.3")):
216            # There is no folder that matches what we're looking for!
217            assert Resources._findLatestDirInPaths([test_folder]) is None
218
219    def test_addRemoveStorageType(self):
220        Resources.addStorageType(9901, "YAY")
221        Resources.addType(9902, "whoo")
222        Resources.addStorageType(100, "herpderp")
223
224        with pytest.raises(ResourceTypeError):
225            # We can't add the same type again
226            Resources.addStorageType(9901, "nghha")
227
228        Resources.removeType(9001)
229        Resources.removeType(9902)
230
231        with pytest.raises(ResourceTypeError):
232            # We can't do that, since it's in the range of user types.
233            Resources.removeType(100)
234
235        with pytest.raises(ResourceTypeError):
236            # We can't do that, since it's in the range of user types.
237            Resources.addType(102, "whoo")
238
239