1 /**
2  * Copyright © 2020 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "ffdc_file.hpp"
17 #include "test_utils.hpp"
18 
19 #include <errno.h>     // for errno
20 #include <fcntl.h>     // for fcntl()
21 #include <string.h>    // for memset(), size_t
22 #include <sys/types.h> // for lseek()
23 #include <unistd.h>    // for read(), write(), lseek(), fcntl(), close()
24 
25 #include <exception>
26 #include <filesystem>
27 
28 #include <gtest/gtest.h>
29 
30 using namespace phosphor::power::regulators;
31 using namespace phosphor::power::regulators::test_utils;
32 namespace fs = std::filesystem;
33 
34 /**
35  * Returns whether the specified file descriptor is valid/open.
36  *
37  * @param[in] fd - File descriptor
38  * @return true if descriptor is valid/open, false otherwise
39  */
40 bool isValid(int fd)
41 {
42     return (fcntl(fd, F_GETFL) != -1) || (errno != EBADF);
43 }
44 
45 TEST(FFDCFileTests, Constructor)
46 {
47     // Test where only the FFDCFormat parameter is specified
48     {
49         FFDCFile file{FFDCFormat::JSON};
50         EXPECT_NE(file.getFileDescriptor(), -1);
51         EXPECT_TRUE(isValid(file.getFileDescriptor()));
52         EXPECT_EQ(file.getFormat(), FFDCFormat::JSON);
53         EXPECT_FALSE(file.getPath().empty());
54         EXPECT_TRUE(fs::exists(file.getPath()));
55         EXPECT_EQ(file.getSubType(), 0);
56         EXPECT_EQ(file.getVersion(), 0);
57     }
58 
59     // Test where all constructor parameters are specified
60     {
61         FFDCFile file{FFDCFormat::Custom, 2, 3};
62         EXPECT_NE(file.getFileDescriptor(), -1);
63         EXPECT_TRUE(isValid(file.getFileDescriptor()));
64         EXPECT_EQ(file.getFormat(), FFDCFormat::Custom);
65         EXPECT_FALSE(file.getPath().empty());
66         EXPECT_TRUE(fs::exists(file.getPath()));
67         EXPECT_EQ(file.getSubType(), 2);
68         EXPECT_EQ(file.getVersion(), 3);
69     }
70 
71     // Note: The case where open() fails currently needs to be tested manually
72 }
73 
74 TEST(FFDCFileTests, GetFileDescriptor)
75 {
76     FFDCFile file{FFDCFormat::JSON};
77     int fd = file.getFileDescriptor();
78     EXPECT_NE(fd, -1);
79     EXPECT_TRUE(isValid(fd));
80 
81     // Write some data to the file
82     char buffer[] = "This is some sample data";
83     size_t count = sizeof(buffer);
84     EXPECT_EQ(write(fd, buffer, count), count);
85 
86     // Seek back to the beginning of the file
87     EXPECT_EQ(lseek(fd, 0, SEEK_SET), 0);
88 
89     // Clear buffer
90     memset(buffer, '\0', count);
91     EXPECT_STREQ(buffer, "");
92 
93     // Read and verify file contents
94     EXPECT_EQ(read(fd, buffer, count), count);
95     EXPECT_STREQ(buffer, "This is some sample data");
96 }
97 
98 TEST(FFDCFileTests, GetFormat)
99 {
100     // Test where 'Text' was specified
101     {
102         FFDCFile file{FFDCFormat::Text};
103         EXPECT_EQ(file.getFormat(), FFDCFormat::Text);
104     }
105 
106     // Test where 'Custom' was specified
107     {
108         FFDCFile file{FFDCFormat::Custom, 2, 3};
109         EXPECT_EQ(file.getFormat(), FFDCFormat::Custom);
110     }
111 }
112 
113 TEST(FFDCFileTests, GetPath)
114 {
115     FFDCFile file{FFDCFormat::JSON};
116     EXPECT_FALSE(file.getPath().empty());
117     EXPECT_TRUE(fs::exists(file.getPath()));
118 }
119 
120 TEST(FFDCFileTests, GetSubType)
121 {
122     // Test where subType was not specified
123     {
124         FFDCFile file{FFDCFormat::JSON};
125         EXPECT_EQ(file.getSubType(), 0);
126     }
127 
128     // Test where subType was specified
129     {
130         FFDCFile file{FFDCFormat::Custom, 3, 2};
131         EXPECT_EQ(file.getSubType(), 3);
132     }
133 }
134 
135 TEST(FFDCFileTests, GetVersion)
136 {
137     // Test where version was not specified
138     {
139         FFDCFile file{FFDCFormat::JSON};
140         EXPECT_EQ(file.getVersion(), 0);
141     }
142 
143     // Test where version was specified
144     {
145         FFDCFile file{FFDCFormat::Custom, 2, 5};
146         EXPECT_EQ(file.getVersion(), 5);
147     }
148 }
149 
150 TEST(FFDCFileTests, Remove)
151 {
152     // Test where works
153     {
154         FFDCFile file{FFDCFormat::JSON};
155         EXPECT_NE(file.getFileDescriptor(), -1);
156         EXPECT_TRUE(isValid(file.getFileDescriptor()));
157         EXPECT_FALSE(file.getPath().empty());
158         EXPECT_TRUE(fs::exists(file.getPath()));
159 
160         int fd = file.getFileDescriptor();
161         fs::path path = file.getPath();
162 
163         file.remove();
164         EXPECT_EQ(file.getFileDescriptor(), -1);
165         EXPECT_TRUE(file.getPath().empty());
166 
167         EXPECT_FALSE(isValid(fd));
168         EXPECT_FALSE(fs::exists(path));
169     }
170 
171     // Test where file was already removed
172     {
173         FFDCFile file{FFDCFormat::JSON};
174         EXPECT_NE(file.getFileDescriptor(), -1);
175         EXPECT_FALSE(file.getPath().empty());
176 
177         file.remove();
178         EXPECT_EQ(file.getFileDescriptor(), -1);
179         EXPECT_TRUE(file.getPath().empty());
180 
181         file.remove();
182         EXPECT_EQ(file.getFileDescriptor(), -1);
183         EXPECT_TRUE(file.getPath().empty());
184     }
185 
186     // Test where closing the file fails
187     {
188         FFDCFile file{FFDCFormat::JSON};
189         int fd = file.getFileDescriptor();
190         EXPECT_TRUE(isValid(fd));
191 
192         EXPECT_EQ(close(fd), 0);
193         EXPECT_FALSE(isValid(fd));
194 
195         try
196         {
197             file.remove();
198             ADD_FAILURE() << "Should not have reached this line.";
199         }
200         catch (const std::exception& e)
201         {
202             EXPECT_NE(std::string{e.what()}.find("Unable to close FFDC file: "),
203                       std::string::npos);
204         }
205     }
206 
207     // Test where deleting the file fails
208     {
209         FFDCFile file{FFDCFormat::JSON};
210         fs::path path = file.getPath();
211         EXPECT_TRUE(fs::exists(path));
212 
213         makeFileUnRemovable(path);
214         try
215         {
216             file.remove();
217             ADD_FAILURE() << "Should not have reached this line.";
218         }
219         catch (const std::exception& e)
220         {
221             // This is expected.  Exception message will vary.
222         }
223         makeFileRemovable(path);
224     }
225 }
226