ソースを参照

Implemented .failed functionality, and upgraded unit tests.


git-svn-id: https://svn.microneil.com/svn/SNFUtility/trunk@15 aa37657e-1934-4a5f-aa6d-2d8eab27ff7c
master
adeniz 12年前
コミット
e40a2be6ec
3個のファイルの変更386行の追加178行の削除
  1. 55
    13
      Common/FileBackup.cpp
  2. 20
    3
      Common/FileBackup.hpp
  3. 311
    162
      CommonTests/TestFileBackup.cpp

+ 55
- 13
Common/FileBackup.cpp ファイルの表示

@@ -22,6 +22,8 @@
const std::string FileBackup::BackupSuffix(".bak");
const std::string FileBackup::FailedSuffix(".failed");
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// End of configuration. /////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -98,6 +100,13 @@ FileBackup::GetBackupFileName(std::string File) {
}
std::string
FileBackup::GetFailedFileName(std::string File) {
return File + FailedSuffix;
}
bool
FileBackup::FileExists(std::string File) {
@@ -126,6 +135,25 @@ FileBackup::FileExists(std::string File) {
void
FileBackup::CreateBackupFile(std::string File) {
std::string FailedFileName;
FailedFileName = GetFailedFileName(File);
if (FileExists(FailedFileName)) { // Removed .failed file?
if (0 != remove(FailedFileName.c_str())) {
std::string Temp;
Temp = "Unable to remove file " + FailedFileName;
Temp += " created from a previous run: ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
}
}
bool ThisFileExists = FileExists(File);
if (ThisFileExists) { // Back up if the file exists.
@@ -196,36 +224,50 @@ FileBackup::RestoreAllFilesFromBackup() {
iFile != OriginalFileExists.end();
iFile++) {
std::string BackupFileName;
std::string FailedFileName;
if (iFile->second) { // Original file existed?
if (FileExists(iFile->first)) {
try { // Yes.
try {
BackupFileName = GetBackupFileName(iFile->first);
FailedFileName = GetFailedFileName(iFile->first);
CopyFile(BackupFileName, iFile->first);
CopyFile(iFile->first, FailedFileName);
} catch (std::exception &e) {
ErrorMessage << "Error restoring " << iFile->first << " from backup "
<< BackupFileName << ": " << e.what() << " \n";
ErrorMessage << "Error copying " << iFile->first << " to "
<< FailedFileName << ": " << e.what() << " \n";
ErrorOccurred = true;
}
if (0 != remove(iFile->first.c_str())) { // Remove the new file.
ErrorMessage << "Unable to remove backup file " << BackupFileName
<< ": " << strerror(errno) << "\n";
ErrorOccurred = true;
}
} else { // No.
}
if (iFile->second) { // Original file existed?
if (FileExists(iFile->first)) {
try { // Yes.
BackupFileName = GetBackupFileName(iFile->first);
if (0 != remove(iFile->first.c_str())) {
CopyFile(BackupFileName, iFile->first);
ErrorMessage << "Unable to remove backup file " << BackupFileName
<< ": " << strerror(errno) << "\n";
} catch (std::exception &e) {
ErrorOccurred = true;
ErrorMessage << "Error restoring " << iFile->first << " from backup "
<< BackupFileName << ": " << e.what() << " \n";
}
ErrorOccurred = true;
}

+ 20
- 3
Common/FileBackup.hpp ファイルの表示

@@ -24,9 +24,9 @@ public:
/// Create a backup of the specified file.
//
// This method creates a backup of the specified file The name of
// the link is the name of the file appended by the BackupSuffix
// data member.
// This method first removes any .failed files, and then creates a
// backup of the specified file. The name of the backup file is
// the name of the file appended by the BackupSuffix data member.
//
// The file can be restored from the backup by RestoreFromBackup.
//
@@ -52,6 +52,10 @@ public:
// This method restores the backup of the files specified by the
// CreateBackupFile() method.
//
// The new file that is overwritten is first copied to the file
// with a suffix ".failed". Then, if the original file existed,
// it is restored.
//
// \throws std::runtime_error if an error is encountered.
//
// \see CreateBackupFile().
@@ -66,6 +70,15 @@ public:
//
static std::string GetBackupFileName(std::string File);
/// Get the name of the failed file.
//
// \param[in] File is the name of the file that contains the
// configuration that failed (i.e. resulted in an error).
//
// \returns the name of the failed file.
//
static std::string GetFailedFileName(std::string File);
/// Check if a file exists.
//
// \returns true if the file exists, false otherwise.
@@ -88,6 +101,10 @@ private:
/// name.
static const std::string BackupSuffix;
/// Suffix to append to the file name to obtain the failed file
/// name.
static const std::string FailedSuffix;
/// Typedef for container of names of files.
//
// The key is the name of the file to back up. The value is true

+ 311
- 162
CommonTests/TestFileBackup.cpp ファイルの表示

@@ -32,7 +32,7 @@
#define ErrorExit(msg) \
{ \
Error(msg) \
std::exit(-1); \
std::exit(-1); \
}
/// Vector of strings
@@ -68,7 +68,7 @@ Initialize() {
for (int i = 0; i < NoFileName.size(); i++) {
remove(NoFileName[i].c_str());
remove(NoFileName[i].c_str());
}
std::vector<int> FileSize;
@@ -85,168 +85,220 @@ Initialize() {
std::vector<int>::iterator iSize = FileSize.begin();
for (StringContainer::iterator iFile = FileName.begin(); // Create random data.
iFile != FileName.end();
iFile++, iSize++) {
iFile != FileName.end();
iFile++, iSize++) {
for (int iChar = 0; iChar < *iSize; iChar++) {
for (int iChar = 0; iChar < *iSize; iChar++) {
FileContent[*iFile].push_back(RandomChar[static_cast<std::size_t>(std::rand() * (1.0 / (RAND_MAX + 1.0 )) * CharLen)]);
FileContent[*iFile].push_back(RandomChar[static_cast<std::size_t>(std::rand() * (1.0 / (RAND_MAX + 1.0 )) * CharLen)]);
}
}
remove(iFile->c_str());
remove(FileBackup::GetBackupFileName(*iFile).c_str());
remove(FileBackup::GetBackupFileName(*iFile).c_str());
remove(FileBackup::GetFailedFileName(*iFile).c_str());
std::ofstream Output; // Write the test file.
Output.open(iFile->c_str(), std::ios::trunc);
if (!Output) {
std::string Temp;
Output.open(iFile->c_str(), std::ios::trunc);
if (!Output) {
std::string Temp;
Temp = "Initialize: Error opening the test file " + *iFile;
Temp += " to copy to: ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
Temp = "Initialize: Error opening the test file " + *iFile;
Temp += " to copy to: ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
}
}
Output << FileContent[*iFile];
if (Output.bad() || Output.fail()) {
std::string Temp;
if (Output.bad() || Output.fail()) {
std::string Temp;
Temp = "Initialize: Error writing the test file " + *iFile;
Temp += ": ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
Temp = "Initialize: Error writing the test file " + *iFile;
Temp += ": ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
}
}
Output.close();
Output.close();
}
}
void
std::string
OverwriteFile(std::string File) {
std::ofstream Output;
std::string NewContent;
Output.open(File.c_str(), std::ios::trunc); // Overwrite the test file.
if (!Output) {
std::string Temp;
std::string Temp;
Temp = "OverwriteFile: Error opening the file " + File;
Temp += " to overwrite: ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
Temp = "OverwriteFile: Error opening the file " + File;
Temp += " to overwrite: ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
}
for (int j = 0; j < 223; j++) {
Output << RandomChar[static_cast<std::size_t>(std::rand() * (1.0 / (RAND_MAX + 1.0 )) * CharLen)];
NewContent.push_back(RandomChar[static_cast<std::size_t>(std::rand() * (1.0 / (RAND_MAX + 1.0 )) * CharLen)]);
}
Output << NewContent; // Overwrite the test file.
if (Output.bad() || Output.fail()) {
std::string Temp;
std::string Temp;
Temp = "OverwriteFile: Error overwriting the file " + File;
Temp += ": ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
Temp = "OverwriteFile: Error overwriting the file " + File;
Temp += ": ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
}
Output.close();
if (!FileBackup::FileExists(File)) {
std::string Temp;
std::string Temp;
Temp = "OverwriteFile: File " + File;
Temp += " was created but was determined to not exist.\n";
throw std::runtime_error(Temp);
Temp = "OverwriteFile: File " + File;
Temp += " was created but was determined to not exist.\n";
throw std::runtime_error(Temp);
}
return NewContent;
}
bool
TestBackupRestore() {
CompareContent(std::string FileName, std::string ExpectedContent) {
for (int i = 0; i < NoFileName.size(); i++) { // Backup and overwrite files that don't exist.
std::ifstream Input;
Input.open(FileName.c_str());
if (!Input) {
std::string Temp;
Temp = "CompareContent: Error opening " + FileName;
Temp += ": ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
TestFileBackup.CreateBackupFile(NoFileName[i]);
OverwriteFile(NoFileName[i]);
}
for (int i = 0; i < FileName.size(); i++) { // Back up and overwrite the files that exist.
std::string ReadContent;
Input >> ReadContent;
TestFileBackup.CreateBackupFile(FileName[i]);
OverwriteFile(FileName[i]);
Input.close();
if (!Input) {
std::string Temp;
Temp = "CompareContent: Error closing " + FileName;
Temp += ": ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
}
TestFileBackup.RestoreAllFilesFromBackup();
if (ReadContent != ExpectedContent) {
std::string Temp;
Temp = "***Error--File " + FileName;
Temp += " did not have the correct content.\n";
Error(Temp);
return false;
}
return true;
}
bool
TestBackupRestore() {
ContentContainer NewContent;
ContentContainer OverwrittenContent;
std::string Content;
bool ResultIsPass = true;
for (int i = 0; i < NoFileName.size(); i++) { // Check that files don't exist.
for (int i = 0; i < NoFileName.size(); i++) { // Backup and overwrite files that don't exist.
TestFileBackup.CreateBackupFile(NoFileName[i]);
Content = OverwriteFile(NoFileName[i]); // Overwrite the file.
NewContent[NoFileName[i]] = Content; // Save the new content.
if (FileBackup::FileExists(NoFileName[i])) {
std::string Temp;
}
Temp = "TestBackupRestore: File " + NoFileName[i];
Temp += " was supposed to be deleted by RestoreAllFilesFromBackup, ";
Temp += "but was determined to exist.\n";
throw std::runtime_error(Temp);
for (int i = 0; i < FileName.size(); i++) { // Back up and overwrite the files that exist.
}
TestFileBackup.CreateBackupFile(FileName[i]);
Content = OverwriteFile(FileName[i]); // Overwrite the file.
OverwrittenContent[FileName[i]] = Content; // Save the new contents.
}
for (int i = 0; i < FileName.size(); i++) { // Check content of restored files.
TestFileBackup.RestoreAllFilesFromBackup();
for (int i = 0; i < NoFileName.size(); i++) {
if (FileBackup::FileExists(NoFileName[i])) { // Check that files don't exist.
std::string Temp;
std::ifstream Input;
Temp = "TestBackupRestore: File " + NoFileName[i];
Temp += " was supposed to be deleted by RestoreAllFilesFromBackup, ";
Temp += "but was determined to exist.\n";
Error(Temp);
ResultIsPass = false;
Input.open(FileName[i].c_str());
if (!Input) {
std::string Temp;
}
Temp = "TestBackupRestore: Error opening the restored file " + FileName[i];
Temp += ": ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
if (!CompareContent(FileBackup::GetFailedFileName(NoFileName[i]), // Check the .failed file content.
NewContent[NoFileName[i]])) {
std::string Temp;
}
Temp = "TestBackupRestore: RestoreAllFilesFromBackup() did not correctly";
Temp += " create the .failed file.\n";
Error(Temp);
ResultIsPass = false;
std::string RestoredContent;
}
Input >> RestoredContent;
}
Input.close();
if (!Input) {
std::string Temp;
for (int i = 0; i < FileName.size(); i++) {
Temp = "TestBackupRestore: Error closing the restored file " + FileName[i];
Temp += ": ";
Temp += strerror(errno);
throw std::runtime_error(Temp);
if (!CompareContent(FileName[i], FileContent[FileName[i]])) { // Check content of restored files.
std::string Temp;
}
Temp = "TestBackupRestore: RestoreAllFilesFromBackup() did not correctly";
Temp += " restore the file.\n";
Error(Temp);
ResultIsPass = false;
if (RestoredContent != FileContent[FileName[i]]) {
std::string Temp;
}
Temp = "***Error--File " + FileName[i];
Temp += " was not restored correctly.\n";
if (!CompareContent(FileBackup::GetFailedFileName(FileName[i]), // Check the .failed file content.
OverwrittenContent[FileName[i]])) {
std::string Temp;
Temp = "TestBackupRestore: RestoreAllFilesFromBackup() did not correctly";
Temp += " create the .failed file.\n";
Error(Temp);
ResultIsPass = false;
}
}
}
@@ -260,37 +312,62 @@ TestRemoveAllBackupFiles() {
TestFileBackup.RemoveAllBackupFiles();
std::string BackupFileName;
std::string FailedFileName;
bool ResultIsPass = true;
for (int i = 0; i < NoFileName.size(); i++) { // Check that files don't exist.
for (int i = 0; i < NoFileName.size(); i++) {
BackupFileName = FileBackup::GetBackupFileName(NoFileName[i]);
BackupFileName = FileBackup::GetBackupFileName(NoFileName[i]);
if (FileBackup::FileExists(BackupFileName)) {
std::string Temp;
if (FileBackup::FileExists(BackupFileName)) { // Check that backup files don't exist.
std::string Temp;
Temp = "***Error--Backup file " + BackupFileName;
Temp += " was not removed.\n";
Error(Temp);
ResultIsPass = false;
Temp = "***Error--Backup file " + BackupFileName;
Temp += " was not removed.\n";
Error(Temp);
ResultIsPass = false;
}
FailedFileName = FileBackup::GetFailedFileName(NoFileName[i]);
}
if (!FileBackup::FileExists(FailedFileName)) { // Check that .failed files exist.
std::string Temp;
Temp = "***Error--Failed file " + FailedFileName;
Temp += " does not exist.\n";
Error(Temp);
ResultIsPass = false;
}
}
for (int i = 0; i < FileName.size(); i++) { // Check that files don't exist.
for (int i = 0; i < FileName.size(); i++) {
BackupFileName = FileBackup::GetBackupFileName(FileName[i]);
BackupFileName = FileBackup::GetBackupFileName(FileName[i]);
if (FileBackup::FileExists(BackupFileName)) {
std::string Temp;
if (FileBackup::FileExists(BackupFileName)) { // Check that backup files don't exist.
std::string Temp;
Temp = "***Error--Backup file " + BackupFileName;
Temp += " was not removed.\n";
Error(Temp);
ResultIsPass = false;
Temp = "***Error--Backup file " + BackupFileName;
Temp += " was not removed.\n";
Error(Temp);
ResultIsPass = false;
}
}
FailedFileName = FileBackup::GetFailedFileName(FileName[i]);
if (!FileBackup::FileExists(FailedFileName)) { // Check that .failed files exist.
std::string Temp;
Temp = "***Error--Failed file " + FailedFileName;
Temp += " does not exist.\n";
Error(Temp);
ResultIsPass = false;
}
}
@@ -305,29 +382,29 @@ TestFileExists() {
for (int i = 0; i < FileName.size(); i++) { // Check that files don't exist.
std::string BackupFileName;
std::string BackupFileName;
BackupFileName = FileBackup::GetBackupFileName(FileName[i]);
BackupFileName = FileBackup::GetBackupFileName(FileName[i]);
if (FileBackup::FileExists(BackupFileName)) {
std::string Temp;
if (FileBackup::FileExists(BackupFileName)) {
std::string Temp;
Temp = "***Error--File " + BackupFileName;
Temp += " incorrectly determined to exist.\n";
Error(Temp);
ResultIsPass = false;
Temp = "***Error--File " + BackupFileName;
Temp += " incorrectly determined to exist.\n";
Error(Temp);
ResultIsPass = false;
}
}
if (!FileBackup::FileExists(FileName[i])) {
std::string Temp;
if (!FileBackup::FileExists(FileName[i])) {
std::string Temp;
Temp = "***Error--File " + FileName[i];
Temp += " incorrectly determined to not exist.\n";
Error(Temp);
ResultIsPass = false;
Temp = "***Error--File " + FileName[i];
Temp += " incorrectly determined to not exist.\n";
Error(Temp);
ResultIsPass = false;
}
}
}
@@ -338,30 +415,61 @@ TestFileExists() {
bool
TestBackupRestoreWithNoFiles() {
ContentContainer NewContent;
std::string Content;
std::string BackupFileName;
/// Unit under test.
FileBackup TestNoFileBackup;
for (int i = 0; i < NoFileName.size(); i++) { // Backup and overwrite files that don't exist.
TestNoFileBackup.CreateBackupFile(NoFileName[i]);
OverwriteFile(NoFileName[i]);
TestNoFileBackup.CreateBackupFile(NoFileName[i]);
Content = OverwriteFile(NoFileName[i]); // Overwrite the file.
NewContent[NoFileName[i]] = Content; // Save the new content.
}
TestNoFileBackup.RestoreAllFilesFromBackup();
bool ResultIsPass = true;
for (int i = 0; i < NoFileName.size(); i++) { // Check that files don't exist.
for (int i = 0; i < NoFileName.size(); i++) {
if (FileBackup::FileExists(NoFileName[i])) { // Check that file doesn't exist.
std::string Temp;
Temp = "TestBackupRestoreWithNoFiles: File " + NoFileName[i];
Temp += " was supposed to be deleted by RestoreAllFilesFromBackup, ";
Temp += "but was determined to exist.\n";
Error(Temp);
ResultIsPass = false;
}
BackupFileName = FileBackup::GetBackupFileName(NoFileName[i]);
if (FileBackup::FileExists(NoFileName[i])) {
std::string Temp;
if (FileBackup::FileExists(BackupFileName)) { // Check that backup file doesn't exist.
std::string Temp;
Temp = "TestBackupRestoreWithNoFiles: File " + NoFileName[i];
Temp += " was supposed to be deleted by RestoreAllFilesFromBackup, ";
Temp += "but was determined to exist.\n";
throw std::runtime_error(Temp);
Temp = "TestBackupRestoreWithNoFilesNoOverwrite: File " + BackupFileName;
Temp += " was not supposed to be created by CreateBackupFile, ";
Temp += "but was determined to exist.\n";
Error(Temp);
ResultIsPass = false;
}
}
if (!CompareContent(FileBackup::GetFailedFileName(NoFileName[i]), // Check the .failed file content.
NewContent[NoFileName[i]])) {
std::string Temp;
Temp = "TestBackupRestoreWithNoFilesNoOverwrite: RestoreAllFilesFromBackup() did not correctly";
Temp += " create the .failed file.\n";
Error(Temp);
ResultIsPass = false;
}
}
@@ -377,24 +485,53 @@ TestBackupRestoreWithNoFilesNoOverwrite() {
for (int i = 0; i < NoFileName.size(); i++) { // Backup and overwrite files that don't exist.
TestNoFileBackup.CreateBackupFile(NoFileName[i]);
TestNoFileBackup.CreateBackupFile(NoFileName[i]);
}
TestNoFileBackup.RestoreAllFilesFromBackup();
bool ResultIsPass = true;
std::string BackupFileName;
std::string FailedFileName;
for (int i = 0; i < NoFileName.size(); i++) {
if (FileBackup::FileExists(NoFileName[i])) { // Check that file don't exist.
std::string Temp;
Temp = "TestBackupRestoreWithNoFilesNoOverwrite: File " + NoFileName[i];
Temp += " was supposed to be deleted by RestoreAllFilesFromBackup, ";
Temp += "but was determined to exist.\n";
Error(Temp);
ResultIsPass = false;
}
for (int i = 0; i < NoFileName.size(); i++) { // Check that files don't exist.
BackupFileName = FileBackup::GetBackupFileName(NoFileName[i]);
if (FileBackup::FileExists(NoFileName[i])) {
std::string Temp;
if (FileBackup::FileExists(BackupFileName)) { // Check that backup file doesn't exist.
std::string Temp;
Temp = "TestBackupRestoreWithNoFilesNoOverwrite: File " + BackupFileName;
Temp += " was not supposed to be created by CreateBackupFile, ";
Temp += "but was determined to exist.\n";
Error(Temp);
ResultIsPass = false;
Temp = "TestBackupRestoreWithNoFilesNoOverwrite: File " + NoFileName[i];
Temp += " was supposed to be deleted by RestoreAllFilesFromBackup, ";
Temp += "but was determined to exist.\n";
throw std::runtime_error(Temp);
}
}
FailedFileName = FileBackup::GetFailedFileName(NoFileName[i]);
if (FileBackup::FileExists(FailedFileName)) { // Check that failed file doesn't exist.
std::string Temp;
Temp = "TestBackupRestoreWithNoFilesNoOverwrite: File " + FailedFileName;
Temp += " was not supposed to be created by RestoreAllFilesFromBackup, ";
Temp += "but was determined to exist.\n";
Error(Temp);
ResultIsPass = false;
}
}
@@ -407,7 +544,17 @@ Finalize() {
for (int i = 0; i < FileName.size(); i++) {
remove(FileName[i].c_str());
remove(FileName[i].c_str());
remove((FileBackup::GetBackupFileName(FileName[i]).c_str()));
remove((FileBackup::GetFailedFileName(FileName[i]).c_str()));
}
for (int i = 0; i < NoFileName.size(); i++) {
remove(NoFileName[i].c_str());
remove((FileBackup::GetBackupFileName(NoFileName[i]).c_str()));
remove((FileBackup::GetFailedFileName(NoFileName[i]).c_str()));
}
@@ -423,47 +570,49 @@ int main(int argc, char* argv[]) {
try { // Catch anything that breaks loose.
Initialize(); // Create test files.
Initialize(); // Create test files.
// Test FileExists.
if (!TestFileExists()) {
ErrorExit("TestFileExists() failure.\n");
}
// Test FileExists.
if (!TestFileExists()) {
ErrorExit("TestFileExists() failure.\n");
}
// Test backup/restore.
if (!TestBackupRestore()) {
ErrorExit("TestBackupRestore() failure.\n");
}
// Test backup/restore.
if (!TestBackupRestore()) {
ErrorExit("TestBackupRestore() failure.\n");
}
// Test cleanup.
if (!TestRemoveAllBackupFiles()) {
ErrorExit("TestRemoveAllBackupFiles() failure.\n");
}
// Test cleanup.
if (!TestRemoveAllBackupFiles()) {
ErrorExit("TestRemoveAllBackupFiles() failure.\n");
}
Finalize(); // Remove test files.
Finalize(); // Remove test files.
// Test backup/restore with no files.
if (!TestBackupRestoreWithNoFiles()) {
ErrorExit("TestBackupRestoreWithNoFiles() failure.\n");
}
// Test backup/restore with no files.
if (!TestBackupRestoreWithNoFiles()) {
ErrorExit("TestBackupRestoreWithNoFiles() failure.\n");
}
// Test backup/restore with no files and no overwriting of files.
if (!TestBackupRestoreWithNoFilesNoOverwrite()) {
ErrorExit("TestBackupRestoreWithNoFilesNoOverwrite() failure.\n");
}
// Test backup/restore with no files and no overwriting of files.
if (!TestBackupRestoreWithNoFilesNoOverwrite()) {
ErrorExit("TestBackupRestoreWithNoFilesNoOverwrite() failure.\n");
}
Finalize();
} // That's all folks.
catch(std::exception& e) { // Report any normal exceptions.
std::cerr << "FileBackup exception: " << e.what() << std::endl;
return -1;
std::cerr << "FileBackup exception: " << e.what() << std::endl;
return -1;
}
catch(...) { // Report any unexpected exceptions.
std::cerr << "Panic! Unknown Exception!" << std::endl;
return -1;
std::cerr << "Panic! Unknown Exception!" << std::endl;
return -1;
}

読み込み中…
キャンセル
保存