#include "stdafx.h" #include "UnitTestFrameWork.h" #include "TestP4BridgeServer.h" #include "TextEncoder.h" #include "../p4bridge/P4BridgeClient.h" #include "../p4bridge/P4BridgeServer.h" #include "../p4bridge/P4BridgeEnviro.h" #include "../p4bridge/P4Connection.h" #include #include #include #include #include #include #include #include // Does not return the right value, but is available in VS2010, and does most of what we want #define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) CREATE_TEST_SUITE(TestP4BridgeServer) TestP4BridgeServer::TestP4BridgeServer(void) { UnitTestSuite::RegisterTest(TestGetConfig, "TestGetConfig"); UnitTestSuite::RegisterTest(TestGetSet, "TestGetSet"); UnitTestSuite::RegisterTest(ServerConnectionTest, "ServerConnectionTest"); UnitTestSuite::RegisterTest(TestUnicodeClientToNonUnicodeServer, "TestUnicodeClientToNonUnicodeServer"); UnitTestSuite::RegisterTest(TestUnicodeUserName, "TestUnicodeUserName"); UnitTestSuite::RegisterTest(TestUntaggedCommand, "TestUntaggedCommand"); UnitTestSuite::RegisterTest(TestTaggedCommand, "TestTaggedCommand"); UnitTestSuite::RegisterTest(TestTextOutCommand, "TestTextOutCommand"); UnitTestSuite::RegisterTest(TestBinaryOutCommand, "TestBinaryOutCommand"); UnitTestSuite::RegisterTest(TestErrorOutCommand, "TestErrorOutCommand"); UnitTestSuite::RegisterTest(TestEnviro, "TestEnviro"); UnitTestSuite::RegisterTest(TestConnectSetClient, "TestConnectSetClient"); UnitTestSuite::RegisterTest(TestIsIgnored, "TestIsIgnored"); UnitTestSuite::RegisterTest(TestSetVars, "TestSetVars"); UnitTestSuite::RegisterTest(TestParallelSync, "TestParallelSync"); UnitTestSuite::RegisterTest(TestDefaultProgramNameAndVersion, "TestDefaultProgramNameAndVersion"); UnitTestSuite::RegisterTest(TestGetTicketFile, "TestGetTicketFile"); UnitTestSuite::RegisterTest(TestSetTicketFile, "TestSetTicketFile"); UnitTestSuite::RegisterTest(TestParallelSyncCallback, "TestParallelSyncCallback"); UnitTestSuite::RegisterTest(TestSetProtocol, "TestSetProtocol"); } TestP4BridgeServer::~TestP4BridgeServer(void) { } char unitTestDir[MAX_PATH]; char unitTestZip[MAX_PATH]; char * TestDir = "c:\\MyTestDir"; char * TestZip = "c:\\MyTestDir\\a.exe"; char * rcp_cmd = "p4d -r C:/MyTestDir -jr checkpoint.1"; char * udb_cmd = "p4d -r C:/MyTestDir -xu"; char * p4d_cmd = "p4d -p6666 -IdUnitTestServer -rC:/MyTestDir -Llog"; char * TestLog = "c:\\MyTestDir\\log"; char * testCfgFile = "C:\\MyTestDir\\admin_space\\myP4Config.txt"; char * testTicketFile = "C:\\MyTestDir\\admin_space\\p4tickets.txt"; char * testCfgDir = "C:\\MyTestDir\\admin_space"; char * testIgnoreFile = "C:\\MyTestDir\\admin_space\\myP4Ignore.txt"; char * testIgnoredFile1 = "C:\\MyTestDir\\admin_space\\foofoofoo.foo"; char * testIgnoredFile2 = "C:\\MyTestDir\\admin_space\\moomoomoo.moo"; char * testProgramName = "BridgeUnitTests"; char * testProgramVer = "1.2.3.4.A.b.C"; void * pi = NULL; P4BridgeServer * ps = NULL; string oldConfig; string oldIgnore; bool TestP4BridgeServer::Setup() { // remove the test directory if it exists UnitTestSuite::rmDir( TestDir ) ; GetCurrentDirectory(sizeof(unitTestDir), unitTestDir); strcpy( unitTestZip, unitTestDir); strcat( unitTestZip, "\\a.exe"); if (!CreateDirectory( TestDir, NULL)) { DWORD err = GetLastError(); printf("GetLastError returns %ld\n",err); return false; } if (!CopyFile(unitTestZip, TestZip, false)) return false; if (!SetCurrentDirectory(TestDir)) return false; pi = UnitTestSuite::RunProgram("a", TestDir, true, true); if (!pi) { SetCurrentDirectory(unitTestDir); return false; } delete pi; pi = UnitTestSuite::RunProgram(rcp_cmd, TestDir, true, true); if (!pi) { SetCurrentDirectory(unitTestDir); return false; } delete pi; pi = UnitTestSuite::RunProgram(udb_cmd, TestDir, true, true); if (!pi) { SetCurrentDirectory(unitTestDir); return false; } delete pi; pi = UnitTestSuite::RunProgram(p4d_cmd, TestDir, false, false); if (!pi) { SetCurrentDirectory(unitTestDir); return false; } // _getch(); return true; } int TestP4BridgeServer::LogCallback(int level, const char *file, int line, const char *msg) { printf("LOG: %d %s:%d %s\n", level, file, line , msg); return 1; } bool TestP4BridgeServer::TearDown(char* testName) { UnitTestSuite::rmDir( testCfgFile ) ; UnitTestSuite::rmDir( testIgnoreFile ) ; if (!oldConfig.empty()) { P4BridgeServer::Set("P4CONFIG", oldConfig.c_str()); oldConfig.clear(); } if (!oldIgnore.empty()) { P4BridgeServer::Set("P4IGNORE", oldIgnore.c_str()); oldIgnore.clear(); } if (pi) UnitTestSuite::EndProcess( (LPPROCESS_INFORMATION) pi ); if (ps) { delete ps; ps = NULL; //p4base::DumpMemoryState("After deleting server"); } SetCurrentDirectory(unitTestDir); UnitTestSuite::rmDir( TestDir ) ; p4base::PrintMemoryState(testName); return true; } bool TestP4BridgeServer::ServerConnectionTest() { P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", ""); ASSERT_NOT_NULL(ps); //ps->SetLogCallFn((LogCallbackFn *) &TestP4BridgeServer::LogCallback); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } return true; } bool TestP4BridgeServer::TestUnicodeClientToNonUnicodeServer() { P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } ASSERT_NOT_EQUAL(ps->unicodeServer(), 1); ps->set_charset("utf8", "utf16le"); char* params[1]; params[0] = "//depot/mycode/*"; ASSERT_FALSE(ps->run_command("files", 3456, 0, params, 1)) P4ClientError * out = ps->get_ui(3456)->GetErrorResults(); ASSERT_STRING_STARTS_WITH(out->Message.c_str(), "Unicode clients require a unicode enabled server.") return true; } bool TestP4BridgeServer::TestUnicodeUserName() { P4ClientError* connectionError = NULL; // create a new server //Aleksey (Alexei) in Cyrillic = "\xD0\x90\xD0\xbb\xD0\xB5\xD0\xBA\xD1\x81\xD0\xB5\xD0\xB9\0" IN utf-8 ps = new P4BridgeServer("localhost:6666", "\xD0\x90\xD0\xBB\xD0\xB5\xD0\xBA\xD1\x81\xD0\xB5\xD0\xB9\0", "pass", "\xD0\x90\xD0\xbb\xD0\xB5\xD0\xBA\xD1\x81\xD0\xB5\xD0\xB9\0"); ASSERT_NOT_NULL(ps); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } ASSERT_FALSE(ps->unicodeServer()); ps->set_charset("utf8", "utf16le"); // ps->set_charset("none", "none"); // This disables client P4CHARSET char* params[1]; params[0] = "//depot/mycode/*"; ASSERT_FALSE(ps->run_command("files", 7, 0, params, 1)) P4ClientError * out = ps->get_ui(7)->GetErrorResults(); ASSERT_STRING_STARTS_WITH(out->Message.c_str(), "Unicode clients require a unicode enabled server.") return true; } bool TestP4BridgeServer::TestUntaggedCommand() { P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255, "Connection error: %s", *connectionError); // Abort if the connect did not succeed ASSERT_FAIL(buff); } char* params[1]; params[0] = "//depot/mycode/*"; ASSERT_INT_TRUE(ps->run_command("files", 7, 0, params, 1)) P4ClientInfoMsg * out = ps->get_ui(7)->GetInfoResults(); ASSERT_STRING_STARTS_WITH(out->Message.c_str(), "//depot/MyCode/") return true; } bool TestP4BridgeServer::TestTaggedCommand() { P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } char* params[1]; params[0] = "//depot/mycode/*"; ASSERT_INT_TRUE(ps->run_command("files", 7, 1, params, 1)) StrDictListIterator * out = ps->get_ui(7)->GetTaggedOutput(); ASSERT_NOT_NULL(out); int itemCnt = 0; while (StrDictList * pItem = out->GetNextItem()) { int entryCnt = 0; while (KeyValuePair * pEntry = out->GetNextEntry()) { ASSERT_TRUE(pEntry->key != "func"); if ((itemCnt == 0) && (strcmp(pEntry->key.c_str(), "depotFile") == 0)) ASSERT_STRING_STARTS_WITH(pEntry->value.c_str(), "//depot/MyCode/") if ((itemCnt == 1) && (strcmp(pEntry->key.c_str(), "depotFile") == 0)) ASSERT_STRING_STARTS_WITH(pEntry->value.c_str(), "//depot/MyCode/") entryCnt++; } ASSERT_NOT_EQUAL(entryCnt, 0); itemCnt++; } ASSERT_EQUAL(itemCnt, 3); delete out; return true; } bool TestP4BridgeServer::TestTextOutCommand() { P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } char* params[1]; params[0] = "//depot/MyCode/ReadMe.txt"; ASSERT_INT_TRUE(ps->run_command("print", 7, 1, params, 1)) const char* out = ps->get_ui(7)->GetTextResults(); ASSERT_NOT_NULL(out); ASSERT_STRING_EQUAL(out, "Don't Read This!\n\nIt's Secret!") return true; } bool TestP4BridgeServer::TestBinaryOutCommand() { P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } char* params[1]; params[0] = "//depot/MyCode/Silly.bmp"; ASSERT_INT_TRUE(ps->run_command("print", 7, 1, params, 1)) size_t cnt = ps->get_ui(7)->GetBinaryResultsCount(); ASSERT_EQUAL(cnt, 3126) const unsigned char * out = ps->get_ui(7)->GetBinaryResults(); ASSERT_NOT_NULL(out); ASSERT_EQUAL((*(((unsigned char*)out) + 1)), 0x4d) return true; } bool TestP4BridgeServer::TestErrorOutCommand() { P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255,"Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } char* params[1]; params[0] = "//depot/MyCode/Billy.bmp"; // run a command against a nonexistent file // Should fail ASSERT_FALSE(ps->run_command("rent", 7, 1, params, 1)) P4ClientError * out = ps->get_ui(7)->GetErrorResults(); ASSERT_NOT_NULL(out); //ASSERT_STRING_STARTS_WITH(out->Message, "Unknown command. Try 'p4 help' for info") ASSERT_EQUAL(out->ErrorCode, 805379098); ASSERT_NULL(out->Next) return true; } bool TestP4BridgeServer::TestGetSet() { char *expected = "C:\\login.bat"; char *test_charset = "utf16"; // first thing: delete P4FOOBAR from the registry P4BridgeServer::Set("P4FOOBAR", NULL); P4BridgeServer::Reload(); P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", ""); ASSERT_NOT_NULL(ps); ps->SetLogCallFn((LogCallbackFn *) &TestP4BridgeServer::LogCallback); ASSERT_NOT_NULL(ps); // set server to use test_charset for file translation ps->set_charset(test_charset); // get charset from server string cset = ps->get_charset(); ASSERT_STRING_EQUAL(test_charset, cset.c_str()); // Test the P4BridgeEnviro Update() function // Any values set with Update should be the first returned. P4BridgeServer::Update("P4FOOBAR", expected); const char* result = P4BridgeServer::Get("P4FOOBAR"); ASSERT_STRING_EQUAL(expected, result); P4BridgeServer::Reload(); result = P4BridgeServer::Get("P4FOOBAR"); ASSERT_NULL(result); wchar_t *w_expected = L"C:\\login<АБВГ>.bat"; expected = TextEncoder::Utf16ToUtf8(w_expected); P4BridgeServer::Set("P4FOOBAR", expected); result = P4BridgeServer::Get("P4FOOBAR"); ASSERT_STRING_EQUAL(expected, result); wchar_t *w_result = TextEncoder::Utf8ToUtf16(result); ASSERT_W_STRING_EQUAL(w_expected, w_result); P4BridgeServer::Set("P4FOOBAR", NULL); result = P4BridgeServer::Get("P4FOOBAR"); // P4-16150, Set + Get on Windows returns cached value ASSERT_NULL(result); // calling ::Set below will change the allocation of the // strptr that ::Get is pointing at. you have to cache // the value (string object) if you need it later const char* _orig = P4BridgeServer::Get("P4CONFIG"); string orig = (_orig == NULL) ? "" : _orig; string actual = "not_a_real_config_setting"; P4BridgeServer::Set("P4CONFIG", actual.c_str()); string getResult = P4BridgeServer::Get("P4CONFIG"); ASSERT_STRING_EQUAL(actual.c_str(), getResult.c_str()); P4BridgeServer::Set("P4CONFIG", orig.c_str()); const char* _getResult = P4BridgeServer::Get("P4CONFIG"); if (!_getResult) { ASSERT_NULL(_orig); } else { ASSERT_STRING_EQUAL(orig.c_str(), _getResult); } return true; } bool TestP4BridgeServer::TestEnviro() { // Save the existing value const char* _existing = P4BridgeServer::Get("P4CONFIG"); string existing = (_existing) ? _existing : ""; // Override the value with Update() P4BridgeServer::Update("P4CONFIG", "myP4Config.txt"); string result1 = P4BridgeServer::Get("P4CONFIG"); printf("result1: %s\n",result1); // Check with a value not in the environment P4BridgeServer::Update("P4NOENV", "myP4NOENV.txt"); string result2 = P4BridgeServer::Get("P4NOENV"); printf("result2: %s\n",result2); ASSERT_STRING_EQUAL(result2.c_str(), "myP4NOENV.txt"); // Now try to clear the value locally P4BridgeServer::Update("P4CONFIG",""); const char* pResult = P4BridgeServer::Get("P4CONFIG"); ASSERT_NULL(pResult); // Now remove the Local override P4BridgeServer::Reload(); const char* result4 = P4BridgeServer::Get("P4CONFIG"); if (!result4) { ASSERT_NULL(_existing); } else { ASSERT_STRING_EQUAL(result4, existing.c_str()); } return true; } bool TestP4BridgeServer::TestConnectSetClient() { ps = new P4BridgeServer("localhost:6666", "admin", NULL, NULL); ASSERT_NOT_NULL(ps); ps->set_client("admin_space"); // check to see that the client is set correctly string client = ps->get_client(); ASSERT_STRING_EQUAL(client.c_str(), "admin_space"); return true; } void WriteConfigFile(const char* file) { std::filebuf fb; fb.open(file, std::ios::out); std::ostream os(&fb); os << "P4CLIENT testClient\r\n"; fb.close(); } bool TestP4BridgeServer::TestGetConfig() { // grab the global enviro's P4CONFIG const char*_oldConfig = P4BridgeServer::Get("P4CONFIG"); if (_oldConfig == NULL || *_oldConfig == 0) _oldConfig = "noconfig"; string oldConfigPath = (string(testCfgDir) + "\\" + _oldConfig); // write 2 config files for the enviro to pick up WriteConfigFile(testCfgFile); WriteConfigFile(oldConfigPath.c_str()); // NOTE: Update() merely caches a value local to the underlying enviro object // If you call Get() it will return that value // If you cause the enviro to reload, the cached value will be lost const char *oldConfig = P4BridgeServer::Get("P4CONFIG"); P4BridgeServer::Update("P4CONFIG", "myP4Config.txt"); const char* cfg = P4BridgeServer::Get("P4CONFIG"); ASSERT_STRING_EQUAL(cfg, "myP4Config.txt"); // get_config should not use the "default" enviro object // other than to get the relevant P4CONFIG value // TODO: test that the P4CONFIG setting is unchanged string result = P4BridgeServer::get_config(testCfgDir); ASSERT_STRING_EQUAL(testCfgFile, result.c_str()); P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // set the cwd to null (should not fail) ps->set_cwd(NULL); // this will reset ps's enviro, which means P4CONFIG will be the default again // but the "global" one should remain the same ps->set_cwd(testCfgDir); cfg = P4BridgeServer::Get("P4CONFIG"); ASSERT_STRING_EQUAL("myP4Config.txt", cfg); result = ps->get_config(); if (strcmp(_oldConfig, "noconfig") != 0) { ASSERT_STRING_EQUAL(oldConfigPath.c_str(), result.c_str()); } else { ASSERT_STRING_EQUAL(_oldConfig, result.c_str()); } return true; } bool TestP4BridgeServer::TestIsIgnored() { OFSTRUCT ReOpenBuff; const char* pIgnore = P4BridgeServer::Get("P4IGNORE"); oldIgnore = (pIgnore ? pIgnore : ""); HFILE hf = OpenFile(testIgnoreFile, &ReOpenBuff, OF_CREATE); DWORD written = 0; WriteFile((HANDLE) hf, (LPVOID) "foofoofoo.foo\r\n", 15, &written, NULL); CloseHandle((HANDLE) hf); P4BridgeServer::Set("P4IGNORE", "myP4Ignore.txt"); ASSERT_INT_TRUE(P4BridgeServer::IsIgnored(StrRef(testIgnoredFile1))); ASSERT_FALSE(P4BridgeServer::IsIgnored(StrRef(testIgnoredFile2))); return true; } // Some experimentation with command options happened in this test. // There is a lot of supporting code which is still there, but unneeded for just the test bool TestP4BridgeServer::TestSetVars() { Error e; // test data for reading Protocols (these are read after client::Init()) char *name = "apilevel"; int value = 38; char *name1 = "server"; char *value1 = "40"; char *name2 = "nocase"; char *value2 = "1"; char *name3 = "security"; char *value3 = "0"; char *name4 = "unicode"; char *value4 = "0"; P4ClientError* connectionError = NULL; StrPtr *pptr = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // enable logging of C++ events //ps->SetLogCallFn((LogCallbackFn *)&TestP4BridgeServer::LogCallback); // Works // Protocol options must be set before client::Init() // -Z options are set by Protocol //ps->SetProtocol("dbstat",""); // "track", "dbstat", "app=name", "proxyverbose" //ps->SetProtocol(P4Tag::v_tag, "yes"); // enable tagged output WORKS // ps->SetProtocol("track", ""); // enable tracking WORKS // connect and see if the api returned an error. if (!ps->connected(&connectionError)) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } P4Connection* client = ps->getConnection(0); P4BridgeClient *bridge_ui = ps->get_ui(0); // What command to send? char * cmdname = "users"; char * const v[2] = {"-m", "10"}; client->SetArgv(2, v); // Interestingly, the "tag" option works as both a Var and a Protocol //client->SetVar(P4Tag::v_tag, "yes"); // enable tagged output WORKS // -z options maxLockTime maxOpenFiles maxResults maxScanRows //client->SetVarV("maxOpenFiles=1"); // -v options are passed by p4debug.SetLevel() calls. // p4debug.SetLevel("rpc=3"); // This will spew debug output to stdout WORKS // p4debug.SetLevel(5); // This will spew ALL debug output to stdout WORKS //p4debug.SetLevel("track=1"); ps->Run_int(client, cmdname, bridge_ui); //p4debug.SetLevel(0); // Disable Debug output WORKS StrDictListIterator * tagout = bridge_ui->GetTaggedOutput(); const char* tout = bridge_ui->GetTextResults(); StrPtr * tcount = bridge_ui->GetDataSet(); const unsigned char * bout = bridge_ui->GetBinaryResults(); size_t bcount = bridge_ui->GetBinaryResultsCount(); P4ClientInfoMsg *iout = bridge_ui->GetInfoResults(); int icount = bridge_ui->GetInfoResultsCount(); P4ClientError *eout = bridge_ui->GetErrorResults(); if (icount > 0 && iout) { P4ClientInfoMsg *imsg = iout; while (imsg) { printf("info: %d %s\n", imsg->Level, imsg->Message); imsg = imsg->Next; } } //ASSERT_NOT_NULL(tagout); if (tagout) // Tagged output { int itemCnt = 0; while (StrDictList * pItem = tagout->GetNextItem()) { int entryCnt = 0; while (KeyValuePair * pEntry = tagout->GetNextEntry()) { printf("tag: %d %s => %s\n", itemCnt, pEntry->key, pEntry->value); entryCnt++; } itemCnt++; } } if (tout) // Text output { printf("text: %s", tout); } int level = ps->APILevel(); if (level < value) // Server2 38 or larger is ok { char buff[256]; snprintf(buff, 255, "API mismatch %d < %d", level, value); ASSERT_FAIL(buff); } pptr = client->GetProtocol(name1); ASSERT_NOT_NULL(pptr); if (strcmp(value1, pptr->Value())) { char buff[256]; snprintf(buff, 255, "Value1 mismatch %s != %s", value1, pptr->Value()); ASSERT_FAIL(buff); } pptr = client->GetProtocol(name2); ASSERT_NOT_NULL(pptr); if (strcmp(value2, pptr->Value())) { char buff[256]; snprintf(buff, 255, "Value2 mismatch %s != %s", value2, pptr->Value()); ASSERT_FAIL(buff); } pptr = client->GetProtocol(name3); ASSERT_NOT_NULL(pptr); if (strcmp(value3, pptr->Value())) { char buff[256]; snprintf(buff, 255, "Value3 mismatch %s != %s", value3, pptr->Value()); ASSERT_FAIL(buff); } pptr = client->GetProtocol(name4); ASSERT_NOT_NULL(pptr); if (strcmp(value4, pptr->Value())) { char buff[256]; snprintf(buff, 255, "Value4 mismatch %s != %s", value4, pptr->Value()); ASSERT_FAIL(buff); } return true; } // A test for Parallel Sync error handling - Job085941 bool TestP4BridgeServer::TestParallelSync() { P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } ASSERT_NOT_EQUAL(ps->unicodeServer(), 1); // ps->set_charset("utf8", "utf16le"); char *args[4]; // run p4 configure set net.parallel.max=4 args[0] = "set"; args[1] = "net.parallel.max=4"; ASSERT_INT_TRUE(ps->run_command("configure", 0, 1, args, 2)); // run p4 configure set dmc=3 args[0] = "set"; args[1] = "dmc=3"; ASSERT_INT_TRUE(ps->run_command("configure", 0, 1, args, 2)); // Paths of interest for the parallel test. std::string ppath = "c:\\MyTestDir\\admin_space\\TestData\\parallel\\"; std::string ppathall = ppath + "..."; std::string ppathnone = ppathall + "#none"; std::string targetpath = ppath + "testFile99.txt"; std::string ppath1 = "c:\\MyTestDir\\admin_space2\\TestData\\parallel\\"; std::string targetpath1 = ppath1 + "testFile99.txt"; // Create the workspace directory for the parallel test CreateDirectory(ppath.c_str(), NULL); // write 500 files of 1K size to the workspace directory int count = 500; for (int i = 0; i < count; i++) { std::ostringstream oss; oss << ppath << "testFile" << i << ".txt"; std::ofstream ofs(oss.str(),std::ios::binary | std::ios::out); ofs.seekp(1020); ofs.write("test", 4); ofs.close(); } // add them to the server args[0] = const_cast(ppathall.c_str()); ASSERT_INT_TRUE(ps->run_command("add",0,1,args,1)); // submit them args[0] = "-d"; args[1] = "\"initial submit of test files\""; args[2] = const_cast(ppathall.c_str()); ASSERT_INT_TRUE(ps->run_command("submit",0,1,args,3)); // Remove all Workspace Files (Sync to NONE) args[0] = "-f"; args[1] = const_cast(ppathnone.c_str()); ASSERT_INT_TRUE(ps->run_command("sync",0,1,args,2)); // Sync just the target file args[0] = "-f"; args[1] = const_cast(targetpath.c_str()); ASSERT_INT_TRUE(ps->run_command("sync",0,1,args,2)); // Change target file to Writable _chmod(targetpath.c_str(), _S_IWRITE | _S_IREAD); // Save current user and client //const StrPtr *user1 = ps->get_user(); //const StrPtr *client1 = ps->get_client(); // Change to a different client ps->set_client("admin_space2"); // Sync target file args[0] = "-f"; args[1] = const_cast(targetpath1.c_str()); ASSERT_INT_TRUE(ps->run_command("sync",0,1,args,2)); // Edit the target file args[0] = const_cast(targetpath1.c_str()); ASSERT_INT_TRUE(ps->run_command("edit",0,1,args,1)); // Submit the target file args[0] = "-d"; args[1] = "\"submit of test file\""; args[2] = const_cast(targetpath1.c_str()); ASSERT_INT_TRUE(ps->run_command("submit",0,1,args,3)); // Back to the original workspace ps->set_client("admin_space"); // Do a parallel sync of the original workspace, causes overwrite error, should fail args[0] = "--parallel"; args[1] = "threads=4,batch=8,batchsize=2000,min=9,minsize=2000"; args[2] = const_cast(ppathall.c_str()); int rv = ps->run_command("sync",0,1,args,3); ASSERT_INT_FALSE(rv); // Expect failure return true; } int __stdcall MyParallelSyncCallbackFn(int* pServer, const char *cmd, const char** pArgs, int argCount, int* dictListIter, int threads) { // stub: print the args out printf("P4BridgeServer: 0x[0x%llu]\n", pServer); printf("Command: %s\n", cmd); printf("Args:\n"); for (int i = 0; i < argCount; i++) { printf("\t%d: %s\n", i, pArgs[i]); } printf("Dictionary:\n"); StrDictListIterator* pDli = (StrDictListIterator*)dictListIter; KeyValuePair* pCur = pDli->GetNextEntry(); while (pCur) { printf("\t%s: %s\n", pCur->key.c_str(), pCur->value.c_str()); pCur = pDli->GetNextEntry(); } printf("Threads: %d\n", threads); // TODO: fill in the param checking from the real callback // we could manually launch p4.exe to run the requested // operations and verify that it worked? // for now just return OK return 0; } // similar to above but with light callback testing bool TestP4BridgeServer::TestParallelSyncCallback() { { // run p4 configure set net.parallel.max=4 P4BridgeServer* pServer = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); char *args[4]; args[0] = "set"; args[1] = "net.parallel.max=4"; ASSERT_INT_TRUE(pServer->run_command("configure", 0, 1, args, 2)); // run p4 configure set dmc=3 (?) args[0] = "set"; args[1] = "dmc=3"; ASSERT_INT_TRUE(pServer->run_command("configure", 0, 1, args, 2)); delete pServer; } // make a new connection to pick up the net.parallel.max value P4BridgeServer* pServer = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); P4Connection* pCon = pServer->getConnection(7); P4BridgeClient * ui = pCon->getUi(); pServer->SetParallelTransferCallbackFn(MyParallelSyncCallbackFn); // positive test here; in this phase it should // - call p4 sync with --parallel set to something useful // - receive a callback with some text message // - the sync will actually fail (because we did not sync anything) const char* args[] = { "--parallel", "threads=4,batch=8,batchsize=1,min=1,minsize=1", "-f", "//..." }; ASSERT_INT_TRUE(pServer->run_command("sync", 7, true, (char**)args, sizeof(args)/sizeof(args[0]))); // verify some of the files are here StrDictListIterator * tagged = pServer->get_ui(7)->GetTaggedOutput(); int itemCnt = 0; while (StrDictList * pItem = tagged->GetNextItem()) { int entryCnt = 0; // Find each local filename from tagged output // and confirm that it exists while (KeyValuePair * pEntry = tagged->GetNextEntry()) { struct stat buffer; if (strcmp(pEntry->key.c_str(), "clientFile") == 0) ASSERT_INT_TRUE((stat(pEntry->value.c_str(), &buffer) == 0)); } itemCnt++; } delete tagged; // negative tests // set a null pointer, which means...use p4.exe instead? pServer->SetParallelTransferCallbackFn((ParallelTransferCallbackFn *)0x00); ASSERT_INT_TRUE(pServer->run_command("sync", 7, true, (char**)args, sizeof(args) / sizeof(args[0]))); // set an invalid pointer, which will fail but gracefully pServer->SetParallelTransferCallbackFn((ParallelTransferCallbackFn *)0xFFFFFFFF); ASSERT_INT_FALSE(pServer->run_command("sync", 7, true, (char**)args, sizeof(args) / sizeof(args[0]))); // now that Client's "errors" member has incremented, run sync again without parallel to make sure that // we don't get an error for a successful run without disconnect const char* args2[] = { "-f", "//..." }; ASSERT_INT_TRUE(pServer->run_command("sync", 7, true, (char**)args2, sizeof(args2) / sizeof(args2[0]))); delete pServer; return true; } bool TestP4BridgeServer::TestDefaultProgramNameAndVersion() { P4ClientError* connectionError = NULL; // create a new server ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // connect and see if the api returned an error. if( !ps->connected( &connectionError ) ) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } char* params[1]; params[0] = "//depot/mycode/*"; //p4base::DumpMemoryState("Before first command run"); ASSERT_INT_TRUE(ps->run_command("files", 6, 1, params, 1)) // should both be set to default ASSERT_STRING_EQUAL(ps->pProgramName.c_str(), "something Application"); ASSERT_STRING_EQUAL(ps->pProgramVer.c_str(), "1, 0, 0, 1"); //p4base::DumpMemoryState("After first command run"); // set both program name and version ps->pProgramName = testProgramName; ps->pProgramVer = testProgramVer; ASSERT_INT_TRUE(ps->run_command("files", 7, 1, params, 1)) // should both be set to supplied strings ASSERT_STRING_EQUAL(ps->pProgramName.c_str(), testProgramName); ASSERT_STRING_EQUAL(ps->pProgramVer.c_str(), testProgramVer); //p4base::DumpMemoryState("After first command run"); // set program name but not version ps->pProgramName = testProgramName; ps->pProgramVer.clear(); ASSERT_INT_TRUE(ps->run_command("files", 8, 1, params, 1)) // only name should be set to supplied strings ASSERT_STRING_EQUAL(ps->pProgramName.c_str(), testProgramName); ASSERT_STRING_NOT_EQUAL(ps->pProgramVer.c_str(), testProgramVer); ASSERT_STRING_EQUAL(ps->pProgramVer.c_str(), "1, 0, 0, 1"); // set program version but not name ps->pProgramName.clear(); ps->pProgramVer = testProgramVer; ASSERT_INT_TRUE(ps->run_command("files", 7, 1, params, 1)) // only version should be set to supplied strings ASSERT_STRING_NOT_EQUAL(ps->pProgramName.c_str(), testProgramName); ASSERT_STRING_EQUAL(ps->pProgramVer.c_str(), testProgramVer); ASSERT_STRING_EQUAL(ps->pProgramName.c_str(), "p4bridge-unit-test.exe"); return true; } bool TestP4BridgeServer::TestGetTicketFile() { string tktfile = P4BridgeServer::GetTicketFile(); ASSERT_TRUE(tktfile.length() != 0); return true; } // Callback to provide password for "login" in TestUnicodeUserName() void _stdcall ProvidePassword2(int cmdId, const char *message, char *response, int responseSize, int noEcho) { strncpy(response, "pass1234", responseSize); } bool TestP4BridgeServer::TestSetTicketFile() { ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); P4ClientError* connectionError = NULL; if (!ps->connected(&connectionError)) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } char* params[] = { "set","security=2" }; ASSERT_INT_TRUE(ps->run_command("configure", 7, 0, params, 2)); //string tktfile = ps->ticketFile; //ASSERT_TRUE(tktfile.length() != 0); //ASSERT_EQUAL(tktfile, testTicketFile); ps->SetPromptCallbackFn(ProvidePassword2); //char* params2[] = { "-P","pass1234" }; ASSERT_INT_TRUE(ps->run_command("passwd", 7, 0, NULL, 0)); delete ps; remove(testTicketFile); ps = new P4BridgeServer("localhost:6666", "admin", "pass1234", "admin_space"); ps->set_ticketFile(testTicketFile); if (!ps->connected(&connectionError)) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } ps->SetPromptCallbackFn(ProvidePassword2); ASSERT_INT_TRUE(ps->run_command("login", 7, 0, NULL, 0)); ASSERT_INT_TRUE(ps->run_command("depots", 7, 0, NULL, 0)); std::filebuf fb; ASSERT_NOT_NULL(fb.open(testTicketFile, std::ios::in)); return true; } bool FindInLog(std::string val) { // scan through the server log looking for 'RpcRecvBuffer pizza = 333333' std::filebuf fb; fb.open(TestLog, std::ios::in); std::istream is(&fb); bool foundIt = false; char buff[4096]; while (!is.eof()) { is.getline(buff, sizeof(buff)); if (val == buff) { return true; } } return false; } bool TestP4BridgeServer::TestSetProtocol() { P4ClientError* connectionError = NULL; // turn on rpc=3 on the server and reconnect { char* args[] = { "set", "rpc=3" }; P4BridgeServer* pServer = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_TRUE(pServer->connected(&connectionError) == 1); ASSERT_INT_TRUE(pServer->run_command("configure", 0, true, args, 2)); delete pServer; } // create a another connection ps = new P4BridgeServer("localhost:6666", "admin", "", "admin_space"); ASSERT_NOT_NULL(ps); // must run SetProtocol before the connection is established // note that for the most part SetProtocol is already handled // in our connection code, but this is for the case when // a parallel sync/transmit response has protocol data for // the new connections // note that the server needs to be started with rpc=3 // set a bogus protocol ps->SetProtocol("pizza", "333333"); // set an api number too ps->SetProtocol("api", "9090909"); // connect and see if the api returned an error. if (!ps->connected(&connectionError)) { char buff[256]; snprintf(buff, 255, "Connection error: %s", connectionError->Message); // Abort if the connect did not succeed ASSERT_FAIL(buff); } ASSERT_INT_TRUE(ps->run_command("info", 0, true, NULL, 0)); // pizza is only in the RpcRecvBuffer, the server will ignore it ASSERT_TRUE(FindInLog("RpcRecvBuffer pizza = 333333")); ASSERT_TRUE(FindInLog("RpcRecvBuffer api = 9090909")); // negative test: call SetProtocol too late (we're connected) ps->SetProtocol("calzone", "444444"); ASSERT_INT_TRUE(ps->run_command("info", 0, true, NULL, 0)); ASSERT_FALSE(FindInLog("RpcRecvBuffer calzone = 444444")); // positive test: reconnnect and check again ASSERT_INT_TRUE(ps->disconnect()); ASSERT_TRUE(ps->connected(&connectionError) == 1); ASSERT_INT_TRUE(ps->run_command("info", 0, true, NULL, 0)); ASSERT_TRUE(FindInLog("RpcRecvBuffer calzone = 444444")); return true; }