<commit_before>/*
 * Copyright (c) 2007-2010 Digital Bazaar, Inc. All rights reserved.
 */
#include "monarch/http/HttpConnectionServicer.h"

#include "monarch/http/HttpRequest.h"
#include "monarch/http/HttpResponse.h"
#include "monarch/io/ByteArrayInputStream.h"
#include "monarch/logging/Logging.h"

#include <cstdlib>

using namespace std;
using namespace monarch::io;
using namespace monarch::http;
using namespace monarch::logging;
using namespace monarch::net;
using namespace monarch::rt;

HttpConnectionServicer::HttpConnectionServicer(const char* serverName)
{
   mServerName = strdup(serverName);
}

HttpConnectionServicer::~HttpConnectionServicer()
{
   free(mServerName);
}

HttpRequestServicer* HttpConnectionServicer::findRequestServicer(
   char* path, ServicerMap& servicerMap)
{
   HttpRequestServicer* rval = NULL;

   // strip any query
   if(path != NULL)
   {
      char* end = strrchr(path, '?');
      if(end != NULL)
      {
         end[0] = 0;
      }
   }

   mRequestServicerLock.lockShared();
   {
      // try to find servicer for path
      ServicerMap::iterator i;
      while(rval == NULL && path != NULL)
      {
         i = servicerMap.find(path);
         if(i != servicerMap.end())
         {
            rval = i->second;
         }
         else if(strlen(path) > 1)
         {
            // try to find servicer at parent path
            char* end = strrchr(path, '/');
            if(end != NULL)
            {
               // if parent is root (end == path), set path to "/"
               // if parent is not root, clear last slash
               end[(end == path) ? 1 : 0] = 0;
            }
            else
            {
               // no path left to search
               path = NULL;
            }
         }
         else
         {
            // no path left to search
            path = NULL;
         }
      }
   }
   mRequestServicerLock.unlockShared();

   return rval;
}

void HttpConnectionServicer::serviceConnection(Connection* c)
{
   // wrap connection, set default timeouts to 30 seconds
   HttpConnection hc(c, false);
   hc.setReadTimeout(30000);
   hc.setWriteTimeout(30000);

   // create request
   HttpRequest* request = hc.createRequest();
   HttpRequestHeader* reqHeader = request->getHeader();

   // create response
   HttpResponse* response = request->createResponse();
   HttpResponseHeader* resHeader = response->getHeader();

   // handle keep-alive (HTTP/1.1 keep-alive is on by default)
   bool keepAlive = true;
   bool noerror = true;
   while(keepAlive && noerror)
   {
      // set defaults
      resHeader->setVersion("HTTP/1.1");
      resHeader->setDate();
      resHeader->setField("Server", mServerName);

      // receive request header
      if((noerror = request->receiveHeader()))
      {
         // check http version
         bool version10 = (strcmp(reqHeader->getVersion(), "HTTP/1.0") == 0);
         bool version11 = (strcmp(reqHeader->getVersion(), "HTTP/1.1") == 0);

         // only version 1.0 and 1.1 supported
         if(version10 || version11)
         {
            // set response version according to request version
            resHeader->setVersion(reqHeader->getVersion());

            // use proxy'd host field if one was used
            // else use host field if one was used
            string host;
            if(reqHeader->getField("X-Forwarded-Host", host) ||
               reqHeader->getField("Host", host))
            {
               resHeader->setField("Host", host);
            }

            // get connection header
            string connHeader;
            if(reqHeader->getField("Connection", connHeader))
            {
               if(strcasecmp(connHeader.c_str(), "close") == 0)
               {
                  keepAlive = false;
               }
               else if(strcasecmp(connHeader.c_str(), "keep-alive") == 0)
               {
                  keepAlive = true;
               }
            }
            else if(version10)
            {
               // if HTTP/1.0 and no keep-alive header, keep-alive is off
               keepAlive = false;
            }

            // get request path and normalize it
            const char* inPath = reqHeader->getPath();
            char outPath[strlen(inPath) + 2];
            HttpRequestServicer::normalizePath(inPath, outPath);

            // find appropriate request servicer for path
            HttpRequestServicer* hrs = NULL;

            // find secure/non-secure servicer
            hrs = hc.isSecure() ?
               findRequestServicer(outPath, mSecureServicers) :
               findRequestServicer(outPath, mNonSecureServicers);

            if(hrs != NULL)
            {
               // service request
               hrs->serviceRequest(request, response);

               // if servicer closed connection, turn off keep-alive
               if(c->isClosed())
               {
                  keepAlive = false;
               }

               // turn off keep-alive if response has close connection field
               if(keepAlive)
               {
                  if(resHeader->getField("Connection", connHeader) &&
                     strcasecmp(connHeader.c_str(), "close") == 0)
                  {
                     keepAlive = false;
                  }
               }
            }
            else
            {
               // no servicer, so send 404 Not Found
               const char* html =
                  "
404 Not Found
";
               resHeader->setStatus(404, "Not Found");
               resHeader->setField("Content-Type", "text/html");
               resHeader->setField("Content-Length", 48);
               resHeader->setField("Connection", "close");
               if((noerror = response->sendHeader()))
               {
                  ByteArrayInputStream is(html, 48);
                  noerror = response->sendBody(&is);
               }
            }
         }
         else
         {
            // send 505 HTTP Version Not Supported
            const char* html =
               ""
               "
505 HTTP Version Not Supported
"
               "";
            resHeader->setStatus(505, "HTTP Version Not Supported");
            resHeader->setField("Content-Type", "text/html");
            resHeader->setField("Content-Length", 65);
            resHeader->setField("Connection", "close");
            if((noerror = response->sendHeader()))
            {
               ByteArrayInputStream is(html, 65);
               noerror = response->sendBody(&is);
            }
         }
      }
      else
      {
         // exception occurred while receiving header
         ExceptionRef e = Exception::get();
         if(e->isType("monarch.net.http.BadHeader") ||
            e->isType("monarch.net.http.BadRequest"))
         {
            // send 400 Bad Request
            const char* html =
               "
400 Bad Request
";
            response->getHeader()->setStatus(400, "Bad Request");
            response->getHeader()->setField("Content-Type", "text/html");
            response->getHeader()->setField("Content-Length", 50);
            response->getHeader()->setField("Connection", "close");
            if(response->sendHeader())
            {
               ByteArrayInputStream is(html, 50);
               response->sendBody(&is);
            }
         }
         // if the exception was an interruption, then send a 503
         else if(e->isType("monarch.io.InterruptedException") ||
                 e->isType("monarch.rt.Interrupted"))
         {
            // send 503 Service Unavailable
            const char* html =
               "
503 Service Unavailable
";
            resHeader->setStatus(503, "Service Unavailable");
            resHeader->setField("Content-Type", "text/html");
            resHeader->setField("Content-Length", 58);
            resHeader->setField("Connection", "close");
            if((noerror = response->sendHeader()))
            {
               ByteArrayInputStream is(html, 58);
               noerror = response->sendBody(&is);
            }
         }
         // if the exception was not a socket error then send an internal
         // server error response
         else if(!e->isType("monarch.net.Socket", true))
         {
            // send 500 Internal Server Error
            const char* html =
               "
500 Internal Server Error
";
            resHeader->setStatus(500, "Internal Server Error");
            resHeader->setField("Content-Type", "text/html");
            resHeader->setField("Content-Length", 60);
            resHeader->setField("Connection", "close");
            if((noerror = response->sendHeader()))
            {
               ByteArrayInputStream is(html, 60);
               noerror = response->sendBody(&is);
            }
         }
         else
         {
            // log socket error
            if(e->getDetails()->hasMember("error"))
            {
               MO_CAT_ERROR(MO_HTTP_CAT,
                  "Connection error: ['%s','%s','%s']\n",
                  e->getMessage(), e->getType(),
                  e->getDetails()["error"]->getString());
            }
            else
            {
               MO_CAT_ERROR(MO_HTTP_CAT,
                  "Connection error: ['%s','%s']\n",
                  e->getMessage(), e->getType());
            }
         }
      }

      if(keepAlive && noerror)
      {
         // set keep-alive timeout (defaults to 5 minutes)
         hc.setReadTimeout(1000 * 60 * 5);

         // clear request and response header fields
         reqHeader->clearFields();
         resHeader->clearFields();
         resHeader->clearStatus();
      }
   }

   // clean up request and response
   delete request;
   delete response;

   // close connection
   hc.close();
}

bool HttpConnectionServicer::addRequestServicer(
   HttpRequestServicer* s, bool secure)
{
   bool rval = false;

   const char* path = s->getPath();

   mRequestServicerLock.lockExclusive();
   {
      if(secure)
      {
         ServicerMap::iterator i = mSecureServicers.find(path);
         if(i == mSecureServicers.end())
         {
            mSecureServicers[path] = s;
            rval = true;
         }
      }
      else
      {
         ServicerMap::iterator i = mNonSecureServicers.find(path);
         if(i == mNonSecureServicers.end())
         {
            mNonSecureServicers[path] = s;
            rval = true;
         }
      }
   }
   mRequestServicerLock.unlockExclusive();

   if(!rval)
   {
      ExceptionRef e = new Exception(
         "Could not add http request servicer. Path already in use.",
         "monarch.net.http.DuplicatePath");
      e->getDetails()["path"] = path;
      Exception::set(e);
   }

   return rval;
}

void HttpConnectionServicer::removeRequestServicer(
   HttpRequestServicer* s, bool secure)
{
   mRequestServicerLock.lockExclusive();
   {
      if(secure)
      {
         mSecureServicers.erase(s->getPath());
      }
      else
      {
         mNonSecureServicers.erase(s->getPath());
      }
   }
   mRequestServicerLock.unlockExclusive();
}

HttpRequestServicer* HttpConnectionServicer::removeRequestServicer(
   const char* path, bool secure)
{
   HttpRequestServicer* rval = NULL;

   mRequestServicerLock.lockExclusive();
   {
      if(secure)
      {
         ServicerMap::iterator i = mSecureServicers.find(path);
         if(i != mSecureServicers.end())
         {
            rval = i->second;
            mSecureServicers.erase(i);
         }
      }
      else
      {
         ServicerMap::iterator i = mNonSecureServicers.find(path);
         if(i != mNonSecureServicers.end())
         {
            rval = i->second;
            mNonSecureServicers.erase(i);
         }
      }
   }
   mRequestServicerLock.unlockExclusive();

   return rval;
}

HttpRequestServicer* HttpConnectionServicer::getRequestServicer(
   const char* path, bool secure)
{
   HttpRequestServicer* rval = NULL;

   mRequestServicerLock.lockExclusive();
   {
      if(secure)
      {
         ServicerMap::iterator i = mSecureServicers.find(path);
         if(i != mSecureServicers.end())
         {
            rval = i->second;
         }
      }
      else
      {
         ServicerMap::iterator i = mNonSecureServicers.find(path);
         if(i != mNonSecureServicers.end())
         {
            rval = i->second;
         }
      }
   }
   mRequestServicerLock.unlockExclusive();

   return rval;
}
<commit_msg>Fixed logging output.<commit_after>/*
 * Copyright (c) 2007-2010 Digital Bazaar, Inc. All rights reserved.
 */
#include "monarch/http/HttpConnectionServicer.h"

#include "monarch/http/HttpRequest.h"
#include "monarch/http/HttpResponse.h"
#include "monarch/io/ByteArrayInputStream.h"
#include "monarch/logging/Logging.h"
#include "monarch/rt/DynamicObjectIterator.h"

#include <cstdlib>

using namespace std;
using namespace monarch::io;
using namespace monarch::http;
using namespace monarch::logging;
using namespace monarch::net;
using namespace monarch::rt;

HttpConnectionServicer::HttpConnectionServicer(const char* serverName)
{
   mServerName = strdup(serverName);
}

HttpConnectionServicer::~HttpConnectionServicer()
{
   free(mServerName);
}

HttpRequestServicer* HttpConnectionServicer::findRequestServicer(
   char* path, ServicerMap& servicerMap)
{
   HttpRequestServicer* rval = NULL;

   // strip any query
   if(path != NULL)
   {
      char* end = strrchr(path, '?');
      if(end != NULL)
      {
         end[0] = 0;
      }
   }

   mRequestServicerLock.lockShared();
   {
      // try to find servicer for path
      ServicerMap::iterator i;
      while(rval == NULL && path != NULL)
      {
         i = servicerMap.find(path);
         if(i != servicerMap.end())
         {
            rval = i->second;
         }
         else if(strlen(path) > 1)
         {
            // try to find servicer at parent path
            char* end = strrchr(path, '/');
            if(end != NULL)
            {
               // if parent is root (end == path), set path to "/"
               // if parent is not root, clear last slash
               end[(end == path) ? 1 : 0] = 0;
            }
            else
            {
               // no path left to search
               path = NULL;
            }
         }
         else
         {
            // no path left to search
            path = NULL;
         }
      }
   }
   mRequestServicerLock.unlockShared();

   return rval;
}

void HttpConnectionServicer::serviceConnection(Connection* c)
{
   // wrap connection, set default timeouts to 30 seconds
   HttpConnection hc(c, false);
   hc.setReadTimeout(30000);
   hc.setWriteTimeout(30000);

   // create request
   HttpRequest* request = hc.createRequest();
   HttpRequestHeader* reqHeader = request->getHeader();

   // create response
   HttpResponse* response = request->createResponse();
   HttpResponseHeader* resHeader = response->getHeader();

   // handle keep-alive (HTTP/1.1 keep-alive is on by default)
   bool keepAlive = true;
   bool noerror = true;
   while(keepAlive && noerror)
   {
      // set defaults
      resHeader->setVersion("HTTP/1.1");
      resHeader->setDate();
      resHeader->setField("Server", mServerName);

      // receive request header
      if((noerror = request->receiveHeader()))
      {
         // check http version
         bool version10 = (strcmp(reqHeader->getVersion(), "HTTP/1.0") == 0);
         bool version11 = (strcmp(reqHeader->getVersion(), "HTTP/1.1") == 0);

         // only version 1.0 and 1.1 supported
         if(version10 || version11)
         {
            // set response version according to request version
            resHeader->setVersion(reqHeader->getVersion());

            // use proxy'd host field if one was used
            // else use host field if one was used
            string host;
            if(reqHeader->getField("X-Forwarded-Host", host) ||
               reqHeader->getField("Host", host))
            {
               resHeader->setField("Host", host);
            }

            // get connection header
            string connHeader;
            if(reqHeader->getField("Connection", connHeader))
            {
               if(strcasecmp(connHeader.c_str(), "close") == 0)
               {
                  keepAlive = false;
               }
               else if(strcasecmp(connHeader.c_str(), "keep-alive") == 0)
               {
                  keepAlive = true;
               }
            }
            else if(version10)
            {
               // if HTTP/1.0 and no keep-alive header, keep-alive is off
               keepAlive = false;
            }

            // get request path and normalize it
            const char* inPath = reqHeader->getPath();
            char outPath[strlen(inPath) + 2];
            HttpRequestServicer::normalizePath(inPath, outPath);

            // find appropriate request servicer for path
            HttpRequestServicer* hrs = NULL;

            // find secure/non-secure servicer
            hrs = hc.isSecure() ?
               findRequestServicer(outPath, mSecureServicers) :
               findRequestServicer(outPath, mNonSecureServicers);

            if(hrs != NULL)
            {
               // service request
               hrs->serviceRequest(request, response);

               // if servicer closed connection, turn off keep-alive
               if(c->isClosed())
               {
                  keepAlive = false;
               }

               // turn off keep-alive if response has close connection field
               if(keepAlive)
               {
                  if(resHeader->getField("Connection", connHeader) &&
                     strcasecmp(connHeader.c_str(), "close") == 0)
                  {
                     keepAlive = false;
                  }
               }
            }
            else
            {
               // no servicer, so send 404 Not Found
               const char* html =
                  "
404 Not Found
";
               resHeader->setStatus(404, "Not Found");
               resHeader->setField("Content-Type", "text/html");
               resHeader->setField("Content-Length", 48);
               resHeader->setField("Connection", "close");
               if((noerror = response->sendHeader()))
               {
                  ByteArrayInputStream is(html, 48);
                  noerror = response->sendBody(&is);
               }
            }
         }
         else
         {
            // send 505 HTTP Version Not Supported
            const char* html =
               ""
               "
505 HTTP Version Not Supported
"
               "";
            resHeader->setStatus(505, "HTTP Version Not Supported");
            resHeader->setField("Content-Type", "text/html");
            resHeader->setField("Content-Length", 65);
            resHeader->setField("Connection", "close");
            if((noerror = response->sendHeader()))
            {
               ByteArrayInputStream is(html, 65);
               noerror = response->sendBody(&is);
            }
         }
      }
      else
      {
         // exception occurred while receiving header
         ExceptionRef e = Exception::get();
         if(e->isType("monarch.net.http.BadHeader") ||
            e->isType("monarch.net.http.BadRequest"))
         {
            // send 400 Bad Request
            const char* html =
               "
400 Bad Request
";
            response->getHeader()->setStatus(400, "Bad Request");
            response->getHeader()->setField("Content-Type", "text/html");
            response->getHeader()->setField("Content-Length", 50);
            response->getHeader()->setField("Connection", "close");
            if(response->sendHeader())
            {
               ByteArrayInputStream is(html, 50);
               response->sendBody(&is);
            }
         }
         // if the exception was an interruption, then send a 503
         else if(e->isType("monarch.io.InterruptedException") ||
                 e->isType("monarch.rt.Interrupted"))
         {
            // send 503 Service Unavailable
            const char* html =
               "
503 Service Unavailable
";
            resHeader->setStatus(503, "Service Unavailable");
            resHeader->setField("Content-Type", "text/html");
            resHeader->setField("Content-Length", 58);
            resHeader->setField("Connection", "close");
            if((noerror = response->sendHeader()))
            {
               ByteArrayInputStream is(html, 58);
               noerror = response->sendBody(&is);
            }
         }
         // if the exception was not a socket error then send an internal
         // server error response
         else if(!e->isType("monarch.net.Socket", true))
         {
            // send 500 Internal Server Error
            const char* html =
               "
500 Internal Server Error
";
            resHeader->setStatus(500, "Internal Server Error");
            resHeader->setField("Content-Type", "text/html");
            resHeader->setField("Content-Length", 60);
            resHeader->setField("Connection", "close");
            if((noerror = response->sendHeader()))
            {
               ByteArrayInputStream is(html, 60);
               noerror = response->sendBody(&is);
            }
         }
         else
         {
            // log socket error
            if(e->getDetails()->hasMember("error"))
            {
               // build error string
               string error;
               DynamicObjectIterator i =
                  e->getDetails()["error"].getIterator();
               while(i->hasNext())
               {
                  error.append(i->next()->getString());
                  if(i->hasNext())
                  {
                     error.push_back(',');
                  }
               }
               MO_CAT_ERROR(MO_HTTP_CAT,
                  "Connection error: ['%s','%s','%s']\n",
                  e->getMessage(), e->getType(), error.c_str());
            }
            else
            {
               MO_CAT_ERROR(MO_HTTP_CAT,
                  "Connection error: ['%s','%s']\n",
                  e->getMessage(), e->getType());
            }
         }
      }

      if(keepAlive && noerror)
      {
         // set keep-alive timeout (defaults to 5 minutes)
         hc.setReadTimeout(1000 * 60 * 5);

         // clear request and response header fields
         reqHeader->clearFields();
         resHeader->clearFields();
         resHeader->clearStatus();
      }
   }

   // clean up request and response
   delete request;
   delete response;

   // close connection
   hc.close();
}

bool HttpConnectionServicer::addRequestServicer(
   HttpRequestServicer* s, bool secure)
{
   bool rval = false;

   const char* path = s->getPath();

   mRequestServicerLock.lockExclusive();
   {
      if(secure)
      {
         ServicerMap::iterator i = mSecureServicers.find(path);
         if(i == mSecureServicers.end())
         {
            mSecureServicers[path] = s;
            rval = true;
         }
      }
      else
      {
         ServicerMap::iterator i = mNonSecureServicers.find(path);
         if(i == mNonSecureServicers.end())
         {
            mNonSecureServicers[path] = s;
            rval = true;
         }
      }
   }
   mRequestServicerLock.unlockExclusive();

   if(!rval)
   {
      ExceptionRef e = new Exception(
         "Could not add http request servicer. Path already in use.",
         "monarch.net.http.DuplicatePath");
      e->getDetails()["path"] = path;
      Exception::set(e);
   }

   return rval;
}

void HttpConnectionServicer::removeRequestServicer(
   HttpRequestServicer* s, bool secure)
{
   mRequestServicerLock.lockExclusive();
   {
      if(secure)
      {
         mSecureServicers.erase(s->getPath());
      }
      else
      {
         mNonSecureServicers.erase(s->getPath());
      }
   }
   mRequestServicerLock.unlockExclusive();
}

HttpRequestServicer* HttpConnectionServicer::removeRequestServicer(
   const char* path, bool secure)
{
   HttpRequestServicer* rval = NULL;

   mRequestServicerLock.lockExclusive();
   {
      if(secure)
      {
         ServicerMap::iterator i = mSecureServicers.find(path);
         if(i != mSecureServicers.end())
         {
            rval = i->second;
            mSecureServicers.erase(i);
         }
      }
      else
      {
         ServicerMap::iterator i = mNonSecureServicers.find(path);
         if(i != mNonSecureServicers.end())
         {
            rval = i->second;
            mNonSecureServicers.erase(i);
         }
      }
   }
   mRequestServicerLock.unlockExclusive();

   return rval;
}

HttpRequestServicer* HttpConnectionServicer::getRequestServicer(
   const char* path, bool secure)
{
   HttpRequestServicer* rval = NULL;

   mRequestServicerLock.lockExclusive();
   {
      if(secure)
      {
         ServicerMap::iterator i = mSecureServicers.find(path);
         if(i != mSecureServicers.end())
         {
            rval = i->second;
         }
      }
      else
      {
         ServicerMap::iterator i = mNonSecureServicers.find(path);
         if(i != mNonSecureServicers.end())
         {
            rval = i->second;
         }
      }
   }
   mRequestServicerLock.unlockExclusive();

   return rval;
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions Inc. 1990-2014.  All rights reserved
 */
//  TEST    Foundation::DataExchange::Other
#include    "Stroika/Foundation/StroikaPreComp.h"

#include    "Stroika/Foundation/DataExchange/Atom.h"
#include    "Stroika/Foundation/Debug/Assertions.h"
#include    "Stroika/Foundation/Debug/Trace.h"

#include    "../TestHarness/SimpleClass.h"
#include    "../TestHarness/TestHarness.h"

using   namespace   Stroika;
using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::DataExchange;

namespace   {
    void    Test1_Atom_()
    {

        {
            Atom<> a = L"d";
            Atom<> b = L"d";
            VerifyTestResult (a == b);
            VerifyTestResult (a.GetPrintName () == L"d");
            VerifyTestResult (a.As<String> () == L"d");
            VerifyTestResult (a.As<wstring> () == L"d");
            VerifyTestResult (not a.empty ());
        }
        {
            VerifyTestResult (Atom<> ().empty ());
        }
        {
            Atom<> a = L"d";
            Atom<> b = L"e";
            VerifyTestResult (a != b);
            VerifyTestResult (not a.empty ());
            Atom<>  c   =   a;
            VerifyTestResult (c == a);
        }
    }
}

namespace   {
    void    DoRegressionTests_ ()
    {
        Test1_Atom_ ();
    }
}

int main (int argc, const char* argv[])
{
    Stroika::TestHarness::Setup ();
    Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
    return EXIT_SUCCESS;
}

<commit_msg>comments<commit_after>/*
 * Copyright(c) Sophist Solutions Inc. 1990-2014.  All rights reserved
 */
//  TEST    Foundation::DataExchange::Other
#include    "Stroika/Foundation/StroikaPreComp.h"

#include    "Stroika/Foundation/DataExchange/Atom.h"
#include    "Stroika/Foundation/Debug/Assertions.h"
#include    "Stroika/Foundation/Debug/Trace.h"

#include    "../TestHarness/SimpleClass.h"
#include    "../TestHarness/TestHarness.h"

using   namespace   Stroika;
using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::DataExchange;

namespace   {
    void    Test1_Atom_()
    {
        {
            Atom<> a = L"d";
            Atom<> b = L"d";
            VerifyTestResult (a == b);
            VerifyTestResult (a.GetPrintName () == L"d");
            VerifyTestResult (a.As<String> () == L"d");
            VerifyTestResult (a.As<wstring> () == L"d");
            VerifyTestResult (not a.empty ());
        }
        {
            VerifyTestResult (Atom<> ().empty ());
        }
        {
            Atom<> a = L"d";
            Atom<> b = L"e";
            VerifyTestResult (a != b);
            VerifyTestResult (not a.empty ());
            Atom<>  c   =   a;
            VerifyTestResult (c == a);
        }
    }
}

namespace   {
    void    DoRegressionTests_ ()
    {
        Test1_Atom_ ();
    }
}

int main (int argc, const char* argv[])
{
    Stroika::TestHarness::Setup ();
    Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
    return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2014.  All rights reserved
 */
//  TEST    Foundation::Execution::ThreadSafetyBuiltinObject
#include    "Stroika/Foundation/StroikaPreComp.h"

#include    <mutex>

#include    "Stroika/Foundation/Characters/String.h"
#include    "Stroika/Foundation/Containers/Collection.h"
#include    "Stroika/Foundation/Containers/Sequence.h"
#include    "Stroika/Foundation/Containers/Set.h"
#include    "Stroika/Foundation/Containers/SortedSet.h"
#include    "Stroika/Foundation/Execution/Thread.h"
#include    "Stroika/Foundation/Math/Common.h"

#include    "../TestHarness/TestHarness.h"

using   namespace   Stroika::Foundation;
using   namespace   Characters;
using   namespace   Containers;

using   Execution::Thread;
using   Execution::WaitableEvent;

namespace {

    /*
     *  To REALLY this this code for thread-safety, use ExternallySynchronizedLock, but to verify it works
     *  without worrying about races, just use mutex.
     */
    struct  no_lock_ {
        void lock () {}
        void unlock () {}
    };

}

namespace {
    void    RunThreads (const initializer_list<Thread>& threads)
    {
        for (Thread i : threads) {
            i.Start ();
        }
        for (Thread i : threads) {
            i.WaitForDone ();
        }
    }
}

namespace {

    template <typename ITERABLE_TYPE, typename LOCK_TYPE>
    Thread  mkIterateOverThread_ (ITERABLE_TYPE* iterable, LOCK_TYPE* lock, unsigned int repeatCount)
    {
        using ElementType   =   typename ITERABLE_TYPE::ElementType;
        return Thread ([iterable, lock, repeatCount] () {
            for (unsigned int i = 0; i < repeatCount; ++i) {
                lock_guard<decltype(*lock)> critSec (*lock);
                for (ElementType e :  *iterable) {
                    ElementType e2 = e; // do something
                }
            }
        });
    };

}

namespace {

    template <typename ITERABLE_TYPE, typename LOCK_TYPE>
    Thread  mkOverwriteThread_ (ITERABLE_TYPE* oneToKeepOverwriting, ITERABLE_TYPE elt1, ITERABLE_TYPE elt2, LOCK_TYPE* lock, unsigned int repeatCount)
    {
        return Thread ([oneToKeepOverwriting, lock, repeatCount, elt1, elt2] () {
            for (unsigned int i = 0; i < repeatCount; ++i) {
                for (int ii = 0; ii <= 100; ++ii) {
                    if (Math::IsOdd (ii)) {
                        lock_guard<decltype(*lock)> critSec (*lock);
                        (*oneToKeepOverwriting) = elt1;
                    }
                    else {
                        lock_guard<decltype(*lock)> critSec (*lock);
                        (*oneToKeepOverwriting) = elt2;
                    }
                }
            }
        });
    };

}

namespace   {
    namespace AssignAndIterateAtSameTimeTest_ {
        template    <typename ITERABLE_TYPE>
        void    DoIt_ (ITERABLE_TYPE elt1, ITERABLE_TYPE elt2, unsigned int repeatCount)
        {
            no_lock_ lock ;
            //mutex lock;
            ITERABLE_TYPE   oneToKeepOverwriting = elt1;
            Thread  iterateThread   =   mkIterateOverThread_ (&oneToKeepOverwriting, &lock, repeatCount);
            Thread  overwriteThread =   mkOverwriteThread_ (&oneToKeepOverwriting, elt1, elt2, &lock, repeatCount);
            RunThreads ({iterateThread, overwriteThread});
        }
    }
    void    AssignAndIterateAtSameTimeTest_1_ ()
    {
        Debug::TraceContextBumper traceCtx (SDKSTR ("AssignAndIterateAtSameTimeTest_1_"));
        AssignAndIterateAtSameTimeTest_::DoIt_<String> (String (L"123456789"), String (L"abcdedfghijkqlmopqrstuvwxyz"), 1000);
        //AssignAndIterateAtSameTimeTest_::DoIt_<Collection<int>> (Collection<int> ({1, 3, 4, 5, 6, 33, 12, 13}), Collection<int> ({4, 5, 6, 33, 12, 13, 1, 3, 99, 33, 4, 5}), 1000);
        AssignAndIterateAtSameTimeTest_::DoIt_<Sequence<int>> (Sequence<int> ({1, 3, 4, 5, 6, 33, 12, 13}), Sequence<int> ({4, 5, 6, 33, 12, 13, 1, 3, 99, 33, 4, 5}), 1000);
        AssignAndIterateAtSameTimeTest_::DoIt_<Set<int>> (Set<int> ({1, 3, 4, 5, 6, 33, 12, 13}), Set<int> ({4, 5, 6, 33, 12, 13, 34, 388, 3, 99, 33, 4, 5}), 1000);
        AssignAndIterateAtSameTimeTest_::DoIt_<SortedSet<int>> (SortedSet<int> ({1, 3, 4, 5, 6, 33, 12, 13}), SortedSet<int> ({4, 5, 6, 33, 12, 13, 34, 388, 3, 99, 33, 4, 5}), 1000);
    }
}

namespace   {
    void    DoRegressionTests_ ()
    {
        AssignAndIterateAtSameTimeTest_1_ ();
    }
}

int     main (int argc, const char* argv[])
{
    Stroika::TestHarness::Setup ();
    Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
    return EXIT_SUCCESS;
}
<commit_msg>code factoring for ThreadSafetyBuiltinObject<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2014.  All rights reserved
 */
//  TEST    Foundation::Execution::ThreadSafetyBuiltinObject
#include    "Stroika/Foundation/StroikaPreComp.h"

#include    <mutex>

#include    "Stroika/Foundation/Characters/String.h"
#include    "Stroika/Foundation/Containers/Collection.h"
#include    "Stroika/Foundation/Containers/Sequence.h"
#include    "Stroika/Foundation/Containers/Set.h"
#include    "Stroika/Foundation/Containers/SortedSet.h"
#include    "Stroika/Foundation/Execution/Thread.h"
#include    "Stroika/Foundation/Math/Common.h"

#include    "../TestHarness/TestHarness.h"

using   namespace   Stroika::Foundation;
using   namespace   Characters;
using   namespace   Containers;

using   Execution::Thread;
using   Execution::WaitableEvent;

namespace {

    /*
     *  To REALLY this this code for thread-safety, use ExternallySynchronizedLock, but to verify it works
     *  without worrying about races, just use mutex.
     */
    struct  no_lock_ {
        void lock () {}
        void unlock () {}
    };

}

namespace {
    void    RunThreads_ (const initializer_list<Thread>& threads)
    {
        for (Thread i : threads) {
            i.Start ();
        }
        for (Thread i : threads) {
            i.WaitForDone ();
        }
    }
}

namespace {

    template <typename ITERABLE_TYPE, typename LOCK_TYPE>
    Thread  mkIterateOverThread_ (ITERABLE_TYPE* iterable, LOCK_TYPE* lock, unsigned int repeatCount)
    {
        using ElementType   =   typename ITERABLE_TYPE::ElementType;
        return Thread ([iterable, lock, repeatCount] () {
            for (unsigned int i = 0; i < repeatCount; ++i) {
                lock_guard<decltype(*lock)> critSec (*lock);
                for (ElementType e :  *iterable) {
                    ElementType e2 = e; // do something
                }
            }
        });
    };

}

namespace {

    template <typename ITERABLE_TYPE, typename LOCK_TYPE>
    Thread  mkOverwriteThread_ (ITERABLE_TYPE* oneToKeepOverwriting, ITERABLE_TYPE elt1, ITERABLE_TYPE elt2, LOCK_TYPE* lock, unsigned int repeatCount)
    {
        return Thread ([oneToKeepOverwriting, lock, repeatCount, elt1, elt2] () {
            for (unsigned int i = 0; i < repeatCount; ++i) {
                for (int ii = 0; ii <= 100; ++ii) {
                    if (Math::IsOdd (ii)) {
                        lock_guard<decltype(*lock)> critSec (*lock);
                        (*oneToKeepOverwriting) = elt1;
                    }
                    else {
                        lock_guard<decltype(*lock)> critSec (*lock);
                        (*oneToKeepOverwriting) = elt2;
                    }
                }
            }
        });
    };

}

namespace   {
    namespace AssignAndIterateAtSameTimeTest_1_ {
        template    <typename ITERABLE_TYPE>
        void    DoItOnce_ (ITERABLE_TYPE elt1, ITERABLE_TYPE elt2, unsigned int repeatCount)
        {
            no_lock_ lock ;
            //mutex lock;
            ITERABLE_TYPE   oneToKeepOverwriting = elt1;
            Thread  iterateThread   =   mkIterateOverThread_ (&oneToKeepOverwriting, &lock, repeatCount);
            Thread  overwriteThread =   mkOverwriteThread_ (&oneToKeepOverwriting, elt1, elt2, &lock, repeatCount);
            RunThreads_ ({iterateThread, overwriteThread});
        }
        void    DoIt ()
        {
            Debug::TraceContextBumper traceCtx (SDKSTR ("AssignAndIterateAtSameTimeTest_1_"));
            DoItOnce_<String> (String (L"123456789"), String (L"abcdedfghijkqlmopqrstuvwxyz"), 1000);
            //DoItOnce_<Collection<int>> (Collection<int> ({1, 3, 4, 5, 6, 33, 12, 13}), Collection<int> ({4, 5, 6, 33, 12, 13, 1, 3, 99, 33, 4, 5}), 1000);
            DoItOnce_<Sequence<int>> (Sequence<int> ({1, 3, 4, 5, 6, 33, 12, 13}), Sequence<int> ({4, 5, 6, 33, 12, 13, 1, 3, 99, 33, 4, 5}), 1000);
            DoItOnce_<Set<int>> (Set<int> ({1, 3, 4, 5, 6, 33, 12, 13}), Set<int> ({4, 5, 6, 33, 12, 13, 34, 388, 3, 99, 33, 4, 5}), 1000);
            DoItOnce_<SortedSet<int>> (SortedSet<int> ({1, 3, 4, 5, 6, 33, 12, 13}), SortedSet<int> ({4, 5, 6, 33, 12, 13, 34, 388, 3, 99, 33, 4, 5}), 1000);
        }
    }
}

namespace   {
    void    DoRegressionTests_ ()
    {
        AssignAndIterateAtSameTimeTest_1_::DoIt ();
    }
}

int     main (int argc, const char* argv[])
{
    Stroika::TestHarness::Setup ();
    Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2022.  All rights reserved
 */
//  TEST    Foundation::DataExchange::Other
#include "Stroika/Foundation/StroikaPreComp.h"

#include "Stroika/Foundation/DataExchange/Atom.h"
#include "Stroika/Foundation/DataExchange/InternetMediaType.h"
#include "Stroika/Foundation/DataExchange/InternetMediaTypeRegistry.h"
#include "Stroika/Foundation/DataExchange/OptionsFile.h"
#include "Stroika/Foundation/Debug/Assertions.h"
#include "Stroika/Foundation/Debug/Trace.h"
#include "Stroika/Foundation/Execution/ModuleGetterSetter.h"
#include "Stroika/Foundation/Execution/Logger.h"
#include "Stroika/Foundation/IO/FileSystem/PathName.h"
#include "Stroika/Foundation/IO/FileSystem/WellKnownLocations.h"
#include "Stroika/Foundation/Streams/ExternallyOwnedMemoryInputStream.h"

#include "../TestHarness/SimpleClass.h"
#include "../TestHarness/TestHarness.h"

using namespace Stroika;
using namespace Stroika::Foundation;
using namespace Stroika::Foundation::DataExchange;

using Execution::ModuleGetterSetter;

namespace {
    void Test1_Atom_ ()
    {
        Debug::TraceContextBumper ctx{"{}::Test1_Atom_"};
        {
            Atom<> a = L"d";
            Atom<> b = L"d";
            VerifyTestResult (a == b);
            VerifyTestResult (a.GetPrintName () == L"d");
            VerifyTestResult (a.As<String> () == L"d");
            VerifyTestResult (a.As<wstring> () == L"d");
            VerifyTestResult (not a.empty ());
        }
        {
            VerifyTestResult (Atom<> ().empty ());
        }
        {
            Atom<> a = L"d";
            Atom<> b = L"e";
            VerifyTestResult (a != b);
            VerifyTestResult (not a.empty ());
            Atom<> c = a;
            VerifyTestResult (c == a);
        }
    }
}

namespace {
    void Test2_OptionsFile_ ()
    {
        Debug::TraceContextBumper ctx{"{}::Test2_OptionsFile_"};
        struct MyData_ {
            bool               fEnabled = false;
            optional<DateTime> fLastSynchronizedAt;
        };
        OptionsFile of{
            L"MyModule",
            [] () -> ObjectVariantMapper {
                ObjectVariantMapper mapper;
                mapper.AddClass<MyData_> (initializer_list<ObjectVariantMapper::StructFieldInfo>{
                    {L"Enabled", StructFieldMetaInfo{&MyData_::fEnabled}},
                    {L"Last-Synchronized-At", StructFieldMetaInfo{&MyData_::fLastSynchronizedAt}},
                });
                return mapper;
            }(),
            OptionsFile::kDefaultUpgrader,
            [] (const String& moduleName, const String& fileSuffix) -> filesystem::path {
                return IO::FileSystem::WellKnownLocations::GetTemporary () / IO::FileSystem::ToPath (moduleName + fileSuffix);
            }};
        MyData_ m = of.Read<MyData_> (MyData_{}); // will return default values if file not present
        of.Write (m);                             // test writing
    }
}

namespace {
    struct MyData_ {
        bool               fEnabled = false;
        optional<DateTime> fLastSynchronizedAt;
    };
    struct ModuleGetterSetter_Implementation_MyData_ {
        ModuleGetterSetter_Implementation_MyData_ ()
            : fOptionsFile_{
                  L"MyModule",
                  [] () -> ObjectVariantMapper {
                      ObjectVariantMapper mapper;
                      mapper.AddClass<MyData_> (initializer_list<ObjectVariantMapper::StructFieldInfo>{
                          {L"Enabled", StructFieldMetaInfo{&MyData_::fEnabled}},
                          {L"Last-Synchronized-At", StructFieldMetaInfo{&MyData_::fLastSynchronizedAt}},
                      });
                      return mapper;
                  }(),
                  OptionsFile::kDefaultUpgrader,
                  [] (const String& moduleName, const String& fileSuffix) -> filesystem::path {
                      // for regression tests write to /tmp
                      return IO::FileSystem::WellKnownLocations::GetTemporary () / IO::FileSystem::ToPath (moduleName + fileSuffix);
                  }}
            , fActualCurrentConfigData_{fOptionsFile_.Read<MyData_> (MyData_{})}
        {
            Set (fActualCurrentConfigData_); // assure derived data (and changed fields etc) up to date
        }
        MyData_ Get () const
        {
            return fActualCurrentConfigData_;
        }
        void Set (const MyData_& v)
        {
            fActualCurrentConfigData_ = v;
            fOptionsFile_.Write (v);
        }

    private:
        OptionsFile fOptionsFile_;
        MyData_     fActualCurrentConfigData_; // automatically initialized just in time, and externally synchronized
    };

    ModuleGetterSetter<MyData_, ModuleGetterSetter_Implementation_MyData_> sModuleConfiguration_;

    void Test3_ModuleGetterSetter_ ()
    {
        Debug::TraceContextBumper ctx{"{}::Test3_ModuleGetterSetter_"};
        if (sModuleConfiguration_.Get ().fEnabled) {
            auto n = sModuleConfiguration_.Get ();
            sModuleConfiguration_.Set (n);
        }
    }
}

namespace Test4_VariantValue_ {
    void RunTests ()
    {
        Debug::TraceContextBumper            ctx{"{}::Test4_VariantValue_"};
        Containers::Collection<VariantValue> vc;
        VariantValue                         vv{vc};
    }
}

namespace {
    namespace Test5_InternetMediaType_ {
        void RunTests ()
        {
            Debug::TraceContextBumper ctx{"{}::Test5_InternetMediaType_"};
            {
                InternetMediaType ct0{L"text/plain"};
                VerifyTestResult (ct0.GetType () == L"text");
                VerifyTestResult (ct0.GetSubType () == L"plain");
                VerifyTestResult (ct0.GetSuffix () == nullopt);

                InternetMediaType ct1{L"text/plain;charset=ascii"};
                VerifyTestResult ((ct1.GetParameters () == Containers::Mapping{Common::KeyValuePair<String, String>{L"charset", L"ascii"}}));
                VerifyTestResult (ct1.GetSuffix () == nullopt);

                InternetMediaType ct2{L"text/plain; charset = ascii"};
                VerifyTestResult (ct1 == ct2);

                InternetMediaType ct3{L"text/plain; charset = \"ascii\""};
                VerifyTestResult (ct1 == ct3);

                InternetMediaType ct4{L"text/plain; charset = \"ASCII\""}; // case insensitive compare key, but not value
                VerifyTestResult (ct1 != ct4);

                InternetMediaType ct5{L"application/vnd.ms-excel"};
                VerifyTestResult (ct5.GetType () == L"application");
                VerifyTestResult (ct5.GetSubType () == L"vnd.ms-excel");
                VerifyTestResult (ct5.GetSuffix () == nullopt);

                InternetMediaType ct6{L"application/mathml+xml"};
                VerifyTestResult (ct6.GetType () == L"application");
                VerifyTestResult (ct6.GetSubType () == L"mathml");
                VerifyTestResult (ct6.GetSuffix () == L"xml");
                VerifyTestResult (ct6.As<wstring> () == L"application/mathml+xml");
            }
            {
                // Example from https://tools.ietf.org/html/rfc2045#page-10 - comments ignored, and quotes on value
                InternetMediaType ct1{L"text/plain; charset=us-ascii (Plain text)"};
                InternetMediaType ct2{L"text/plain; charset=\"us-ascii\""};
                VerifyTestResult (ct1 == ct2);
                VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (ct1));
            }
            {
                auto dumpCT = [] (const String& label, InternetMediaType i) {
                    [[maybe_unused]] InternetMediaTypeRegistry r = InternetMediaTypeRegistry::Get ();
                    DbgTrace (L"SUFFIX(%s)=%s", label.c_str (), Characters::ToString (r.GetPreferredAssociatedFileSuffix (i)).c_str ());
                    DbgTrace (L"ASSOCFILESUFFIXES(%s)=%s", label.c_str (), Characters::ToString (r.GetAssociatedFileSuffixes (i)).c_str ());
                    DbgTrace (L"GetAssociatedPrettyName(%s)=%s", label.c_str (), Characters::ToString (r.GetAssociatedPrettyName (i)).c_str ());
                };
                auto checkCT = [] (InternetMediaType i, const Set<String>& possibleFileSuffixes) {
                    [[maybe_unused]] InternetMediaTypeRegistry r = InternetMediaTypeRegistry::Get ();
                    using namespace Characters;
                    if (not possibleFileSuffixes.Contains (r.GetPreferredAssociatedFileSuffix (i).value_or (L""))) {
                        Stroika::TestHarness::WarnTestIssue (
                            Format (L"File suffix mismatch for %s: got %s, expected %s", ToString (i).c_str (), ToString (r.GetPreferredAssociatedFileSuffix (i)).c_str (), ToString (possibleFileSuffixes).c_str ()).c_str ());
                    }
                    if (not possibleFileSuffixes.Any ([&] (String suffix) -> bool { return r.GetAssociatedContentType (suffix) == i; })) {
                        Stroika::TestHarness::WarnTestIssue (
                            Format (L"GetAssociatedContentType for fileSuffixes %s (expected %s, got %s)",
                                    ToString (possibleFileSuffixes).c_str (),
                                    ToString (i).c_str (),
                                    ToString (possibleFileSuffixes.Select<InternetMediaType> ([&] (String suffix) { return r.GetAssociatedContentType (suffix); }).As<Set<InternetMediaType>> ()).c_str ())
                                .c_str ());
                    }
                };
                dumpCT (L"PLAINTEXT", InternetMediaTypes::kText_PLAIN);
                checkCT (InternetMediaTypes::kText_PLAIN, {L".txt"});
                dumpCT (L"HTML", InternetMediaTypes::kHTML);
                checkCT (InternetMediaTypes::kHTML, {L".html", L".htm"});
                dumpCT (L"JSON", InternetMediaTypes::kJSON);
                checkCT (InternetMediaTypes::kJSON, {L".json"});
                dumpCT (L"PNG", InternetMediaTypes::kPNG);
                checkCT (InternetMediaTypes::kPNG, {L".png"});
                {
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsImageFormat (InternetMediaTypes::kPNG));
                    VerifyTestResult (not InternetMediaTypeRegistry::Get ().IsImageFormat (InternetMediaTypes::kJSON));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsXMLFormat (InternetMediaTypes::kXML));
                    VerifyTestResult (not InternetMediaTypeRegistry::Get ().IsXMLFormat (InternetMediaTypes::kText_PLAIN));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kText_PLAIN));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kXML));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kHTML));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kJSON));
                    VerifyTestResult (not InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kPNG));
                    VerifyTestResult (not InternetMediaTypeRegistry::Get ().IsXMLFormat (InternetMediaType{L"text/foobar"}));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsXMLFormat (InternetMediaType{L"text/foobar+xml"}));
                }
            }
            {
                Debug::TraceContextBumper ctx1{"InternetMediaTypeRegistry::Get ().GetMediaTypes()"};
                // enumerate all content types
                for (auto ct : InternetMediaTypeRegistry::Get ().GetMediaTypes ()) {
                    DbgTrace (L"i=%s", Characters::ToString (ct).c_str ());
                }
            }
            {
                Debug::TraceContextBumper ctx1{"InternetMediaTypeRegistry - updating"};
                InternetMediaTypeRegistry origRegistry    = InternetMediaTypeRegistry::Get ();
                InternetMediaTypeRegistry updatedRegistry = origRegistry;
                const auto                kHFType_        = InternetMediaType{L"application/fake-heatlthframe-phr+xml"};
                VerifyTestResult (not InternetMediaTypeRegistry::Get ().GetMediaTypes ().Contains (kHFType_));
                updatedRegistry.AddOverride (kHFType_, InternetMediaTypeRegistry::OverrideRecord{nullopt, Containers::Set<String>{L".HPHR"}, L".HPHR"});
                InternetMediaTypeRegistry::Set (updatedRegistry);
                VerifyTestResult (InternetMediaTypeRegistry::Get ().IsXMLFormat (kHFType_));
                VerifyTestResult (InternetMediaTypeRegistry::Get ().GetMediaTypes ().Contains (kHFType_));
                VerifyTestResult (not origRegistry.GetMediaTypes ().Contains (kHFType_));
                VerifyTestResult (updatedRegistry.GetMediaTypes ().Contains (kHFType_));
            }
        }
    }
}

namespace {
    void DoRegressionTests_ ()
    {
        Test1_Atom_ ();
        Test2_OptionsFile_ ();
        Test3_ModuleGetterSetter_ ();
        Test4_VariantValue_::RunTests ();
        Test5_InternetMediaType_::RunTests ();
    }
}

int main ([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[])
{
    Execution::Logger::Activator logMgrActivator;   // for OptionsFile test
    Stroika::TestHarness::Setup ();
    return Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
}
<commit_msg>Cosmetic<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2022.  All rights reserved
 */
//  TEST    Foundation::DataExchange::Other
#include "Stroika/Foundation/StroikaPreComp.h"

#include "Stroika/Foundation/DataExchange/Atom.h"
#include "Stroika/Foundation/DataExchange/InternetMediaType.h"
#include "Stroika/Foundation/DataExchange/InternetMediaTypeRegistry.h"
#include "Stroika/Foundation/DataExchange/OptionsFile.h"
#include "Stroika/Foundation/Debug/Assertions.h"
#include "Stroika/Foundation/Debug/Trace.h"
#include "Stroika/Foundation/Execution/Logger.h"
#include "Stroika/Foundation/Execution/ModuleGetterSetter.h"
#include "Stroika/Foundation/IO/FileSystem/PathName.h"
#include "Stroika/Foundation/IO/FileSystem/WellKnownLocations.h"
#include "Stroika/Foundation/Streams/ExternallyOwnedMemoryInputStream.h"

#include "../TestHarness/SimpleClass.h"
#include "../TestHarness/TestHarness.h"

using namespace Stroika;
using namespace Stroika::Foundation;
using namespace Stroika::Foundation::DataExchange;

using Execution::ModuleGetterSetter;

namespace {
    void Test1_Atom_ ()
    {
        Debug::TraceContextBumper ctx{"{}::Test1_Atom_"};
        {
            Atom<> a = L"d";
            Atom<> b = L"d";
            VerifyTestResult (a == b);
            VerifyTestResult (a.GetPrintName () == L"d");
            VerifyTestResult (a.As<String> () == L"d");
            VerifyTestResult (a.As<wstring> () == L"d");
            VerifyTestResult (not a.empty ());
        }
        {
            VerifyTestResult (Atom<> ().empty ());
        }
        {
            Atom<> a = L"d";
            Atom<> b = L"e";
            VerifyTestResult (a != b);
            VerifyTestResult (not a.empty ());
            Atom<> c = a;
            VerifyTestResult (c == a);
        }
    }
}

namespace {
    void Test2_OptionsFile_ ()
    {
        Debug::TraceContextBumper ctx{"{}::Test2_OptionsFile_"};
        struct MyData_ {
            bool               fEnabled = false;
            optional<DateTime> fLastSynchronizedAt;
        };
        OptionsFile of{
            L"MyModule",
            [] () -> ObjectVariantMapper {
                ObjectVariantMapper mapper;
                mapper.AddClass<MyData_> (initializer_list<ObjectVariantMapper::StructFieldInfo>{
                    {L"Enabled", StructFieldMetaInfo{&MyData_::fEnabled}},
                    {L"Last-Synchronized-At", StructFieldMetaInfo{&MyData_::fLastSynchronizedAt}},
                });
                return mapper;
            }(),
            OptionsFile::kDefaultUpgrader,
            [] (const String& moduleName, const String& fileSuffix) -> filesystem::path {
                return IO::FileSystem::WellKnownLocations::GetTemporary () / IO::FileSystem::ToPath (moduleName + fileSuffix);
            }};
        MyData_ m = of.Read<MyData_> (MyData_{}); // will return default values if file not present
        of.Write (m);                             // test writing
    }
}

namespace {
    struct MyData_ {
        bool               fEnabled = false;
        optional<DateTime> fLastSynchronizedAt;
    };
    struct ModuleGetterSetter_Implementation_MyData_ {
        ModuleGetterSetter_Implementation_MyData_ ()
            : fOptionsFile_{
                  L"MyModule",
                  [] () -> ObjectVariantMapper {
                      ObjectVariantMapper mapper;
                      mapper.AddClass<MyData_> (initializer_list<ObjectVariantMapper::StructFieldInfo>{
                          {L"Enabled", StructFieldMetaInfo{&MyData_::fEnabled}},
                          {L"Last-Synchronized-At", StructFieldMetaInfo{&MyData_::fLastSynchronizedAt}},
                      });
                      return mapper;
                  }(),
                  OptionsFile::kDefaultUpgrader,
                  [] (const String& moduleName, const String& fileSuffix) -> filesystem::path {
                      // for regression tests write to /tmp
                      return IO::FileSystem::WellKnownLocations::GetTemporary () / IO::FileSystem::ToPath (moduleName + fileSuffix);
                  }}
            , fActualCurrentConfigData_{fOptionsFile_.Read<MyData_> (MyData_{})}
        {
            Set (fActualCurrentConfigData_); // assure derived data (and changed fields etc) up to date
        }
        MyData_ Get () const
        {
            return fActualCurrentConfigData_;
        }
        void Set (const MyData_& v)
        {
            fActualCurrentConfigData_ = v;
            fOptionsFile_.Write (v);
        }

    private:
        OptionsFile fOptionsFile_;
        MyData_     fActualCurrentConfigData_; // automatically initialized just in time, and externally synchronized
    };

    ModuleGetterSetter<MyData_, ModuleGetterSetter_Implementation_MyData_> sModuleConfiguration_;

    void Test3_ModuleGetterSetter_ ()
    {
        Debug::TraceContextBumper ctx{"{}::Test3_ModuleGetterSetter_"};
        if (sModuleConfiguration_.Get ().fEnabled) {
            auto n = sModuleConfiguration_.Get ();
            sModuleConfiguration_.Set (n);
        }
    }
}

namespace Test4_VariantValue_ {
    void RunTests ()
    {
        Debug::TraceContextBumper            ctx{"{}::Test4_VariantValue_"};
        Containers::Collection<VariantValue> vc;
        VariantValue                         vv{vc};
    }
}

namespace {
    namespace Test5_InternetMediaType_ {
        void RunTests ()
        {
            Debug::TraceContextBumper ctx{"{}::Test5_InternetMediaType_"};
            {
                InternetMediaType ct0{L"text/plain"};
                VerifyTestResult (ct0.GetType () == L"text");
                VerifyTestResult (ct0.GetSubType () == L"plain");
                VerifyTestResult (ct0.GetSuffix () == nullopt);

                InternetMediaType ct1{L"text/plain;charset=ascii"};
                VerifyTestResult ((ct1.GetParameters () == Containers::Mapping{Common::KeyValuePair<String, String>{L"charset", L"ascii"}}));
                VerifyTestResult (ct1.GetSuffix () == nullopt);

                InternetMediaType ct2{L"text/plain; charset = ascii"};
                VerifyTestResult (ct1 == ct2);

                InternetMediaType ct3{L"text/plain; charset = \"ascii\""};
                VerifyTestResult (ct1 == ct3);

                InternetMediaType ct4{L"text/plain; charset = \"ASCII\""}; // case insensitive compare key, but not value
                VerifyTestResult (ct1 != ct4);

                InternetMediaType ct5{L"application/vnd.ms-excel"};
                VerifyTestResult (ct5.GetType () == L"application");
                VerifyTestResult (ct5.GetSubType () == L"vnd.ms-excel");
                VerifyTestResult (ct5.GetSuffix () == nullopt);

                InternetMediaType ct6{L"application/mathml+xml"};
                VerifyTestResult (ct6.GetType () == L"application");
                VerifyTestResult (ct6.GetSubType () == L"mathml");
                VerifyTestResult (ct6.GetSuffix () == L"xml");
                VerifyTestResult (ct6.As<wstring> () == L"application/mathml+xml");
            }
            {
                // Example from https://tools.ietf.org/html/rfc2045#page-10 - comments ignored, and quotes on value
                InternetMediaType ct1{L"text/plain; charset=us-ascii (Plain text)"};
                InternetMediaType ct2{L"text/plain; charset=\"us-ascii\""};
                VerifyTestResult (ct1 == ct2);
                VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (ct1));
            }
            {
                auto dumpCT = [] (const String& label, InternetMediaType i) {
                    [[maybe_unused]] InternetMediaTypeRegistry r = InternetMediaTypeRegistry::Get ();
                    DbgTrace (L"SUFFIX(%s)=%s", label.c_str (), Characters::ToString (r.GetPreferredAssociatedFileSuffix (i)).c_str ());
                    DbgTrace (L"ASSOCFILESUFFIXES(%s)=%s", label.c_str (), Characters::ToString (r.GetAssociatedFileSuffixes (i)).c_str ());
                    DbgTrace (L"GetAssociatedPrettyName(%s)=%s", label.c_str (), Characters::ToString (r.GetAssociatedPrettyName (i)).c_str ());
                };
                auto checkCT = [] (InternetMediaType i, const Set<String>& possibleFileSuffixes) {
                    [[maybe_unused]] InternetMediaTypeRegistry r = InternetMediaTypeRegistry::Get ();
                    using namespace Characters;
                    if (not possibleFileSuffixes.Contains (r.GetPreferredAssociatedFileSuffix (i).value_or (L""))) {
                        Stroika::TestHarness::WarnTestIssue (
                            Format (L"File suffix mismatch for %s: got %s, expected %s", ToString (i).c_str (), ToString (r.GetPreferredAssociatedFileSuffix (i)).c_str (), ToString (possibleFileSuffixes).c_str ()).c_str ());
                    }
                    if (not possibleFileSuffixes.Any ([&] (String suffix) -> bool { return r.GetAssociatedContentType (suffix) == i; })) {
                        Stroika::TestHarness::WarnTestIssue (
                            Format (L"GetAssociatedContentType for fileSuffixes %s (expected %s, got %s)",
                                    ToString (possibleFileSuffixes).c_str (),
                                    ToString (i).c_str (),
                                    ToString (possibleFileSuffixes.Select<InternetMediaType> ([&] (String suffix) { return r.GetAssociatedContentType (suffix); }).As<Set<InternetMediaType>> ()).c_str ())
                                .c_str ());
                    }
                };
                dumpCT (L"PLAINTEXT", InternetMediaTypes::kText_PLAIN);
                checkCT (InternetMediaTypes::kText_PLAIN, {L".txt"});
                dumpCT (L"HTML", InternetMediaTypes::kHTML);
                checkCT (InternetMediaTypes::kHTML, {L".html", L".htm"});
                dumpCT (L"JSON", InternetMediaTypes::kJSON);
                checkCT (InternetMediaTypes::kJSON, {L".json"});
                dumpCT (L"PNG", InternetMediaTypes::kPNG);
                checkCT (InternetMediaTypes::kPNG, {L".png"});
                {
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsImageFormat (InternetMediaTypes::kPNG));
                    VerifyTestResult (not InternetMediaTypeRegistry::Get ().IsImageFormat (InternetMediaTypes::kJSON));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsXMLFormat (InternetMediaTypes::kXML));
                    VerifyTestResult (not InternetMediaTypeRegistry::Get ().IsXMLFormat (InternetMediaTypes::kText_PLAIN));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kText_PLAIN));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kXML));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kHTML));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kJSON));
                    VerifyTestResult (not InternetMediaTypeRegistry::Get ().IsTextFormat (InternetMediaTypes::kPNG));
                    VerifyTestResult (not InternetMediaTypeRegistry::Get ().IsXMLFormat (InternetMediaType{L"text/foobar"}));
                    VerifyTestResult (InternetMediaTypeRegistry::Get ().IsXMLFormat (InternetMediaType{L"text/foobar+xml"}));
                }
            }
            {
                Debug::TraceContextBumper ctx1{"InternetMediaTypeRegistry::Get ().GetMediaTypes()"};
                // enumerate all content types
                for (auto ct : InternetMediaTypeRegistry::Get ().GetMediaTypes ()) {
                    DbgTrace (L"i=%s", Characters::ToString (ct).c_str ());
                }
            }
            {
                Debug::TraceContextBumper ctx1{"InternetMediaTypeRegistry - updating"};
                InternetMediaTypeRegistry origRegistry    = InternetMediaTypeRegistry::Get ();
                InternetMediaTypeRegistry updatedRegistry = origRegistry;
                const auto                kHFType_        = InternetMediaType{L"application/fake-heatlthframe-phr+xml"};
                VerifyTestResult (not InternetMediaTypeRegistry::Get ().GetMediaTypes ().Contains (kHFType_));
                updatedRegistry.AddOverride (kHFType_, InternetMediaTypeRegistry::OverrideRecord{nullopt, Containers::Set<String>{L".HPHR"}, L".HPHR"});
                InternetMediaTypeRegistry::Set (updatedRegistry);
                VerifyTestResult (InternetMediaTypeRegistry::Get ().IsXMLFormat (kHFType_));
                VerifyTestResult (InternetMediaTypeRegistry::Get ().GetMediaTypes ().Contains (kHFType_));
                VerifyTestResult (not origRegistry.GetMediaTypes ().Contains (kHFType_));
                VerifyTestResult (updatedRegistry.GetMediaTypes ().Contains (kHFType_));
            }
        }
    }
}

namespace {
    void DoRegressionTests_ ()
    {
        Test1_Atom_ ();
        Test2_OptionsFile_ ();
        Test3_ModuleGetterSetter_ ();
        Test4_VariantValue_::RunTests ();
        Test5_InternetMediaType_::RunTests ();
    }
}

int main ([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[])
{
    Execution::Logger::Activator logMgrActivator; // for OptionsFile test
    Stroika::TestHarness::Setup ();
    return Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2020.  All rights reserved
 */
//  TEST    Foundation::Execution::Other
#include "Stroika/Foundation/StroikaPreComp.h"

#include "Stroika/Foundation/DataExchange/ObjectVariantMapper.h"
#include "Stroika/Foundation/DataExchange/OptionsFile.h"
#include "Stroika/Foundation/Debug/Assertions.h"
#include "Stroika/Foundation/Debug/Trace.h"
#include "Stroika/Foundation/Execution/CommandLine.h"
#include "Stroika/Foundation/Execution/Finally.h"
#include "Stroika/Foundation/Execution/Function.h"
#include "Stroika/Foundation/Execution/ModuleGetterSetter.h"
#include "Stroika/Foundation/Execution/VirtualConstant.h"
#include "Stroika/Foundation/Time/DateTime.h"
#include "Stroika/Foundation/Time/Duration.h"

#include "../TestHarness/SimpleClass.h"
#include "../TestHarness/TestHarness.h"

using namespace Stroika;
using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Execution;

// must be tested before main, so cannot call directly below
namespace {
    int        TestAtomicInitializedCoorectly_ ();
    static int sIgnoredTestValue_ = TestAtomicInitializedCoorectly_ (); // if using static constructors, this will be called before sAtomicBoolNotInitializedTilAfterStaticInitizers_

    atomic<bool> sAtomicBoolNotInitializedTilAfterStaticInitizers_{true}; // for calls before start of or after end of main ()
    int          TestAtomicInitializedCoorectly_ ()
    {
#if qCompilerAndStdLib_atomic_bool_initialize_before_main_Buggy
        if (sAtomicBoolNotInitializedTilAfterStaticInitizers_) {
#if qDebug
            // bug seems to happen just with DEBUG builds - haven't dug into why
            Stroika::TestHarness::WarnTestIssue ("qCompilerAndStdLib_atomic_bool_initialize_before_main_Buggy MAYBE fixed");
#endif
        }
#else
        VerifyTestResult (sAtomicBoolNotInitializedTilAfterStaticInitizers_);
#endif
        return 1;
    }
}

namespace {
    void Test1_Function_ ()
    {
        // Make sure Function<> works as well as std::function
        {
            Function<int (bool)> f = [] ([[maybe_unused]] bool b) -> int { return 3; };
            VerifyTestResult (f (true) == 3);
            function<int (bool)> ff = f;
            VerifyTestResult (ff (true) == 3);
        }
        // Make sure Function<> serves its one purpose - being comparable
        {
            Function<int (bool)> f1 = [] ([[maybe_unused]] bool b) -> int { return 3; };
            Function<int (bool)> f2 = [] ([[maybe_unused]] bool b) -> int { return 3; };

            VerifyTestResult (f1 != f2);
            VerifyTestResult (f1 < f2 or f2 < f1);
            Function<int (bool)> f3 = f1;
            VerifyTestResult (f3 == f1);
            VerifyTestResult (f3 != f2);
        }
    }
}

namespace {
    void Test2_CommandLine_ ()
    {
        {
            String           cmdLine = L"/bin/sh -c \"a b c\"";
            Sequence<String> l       = ParseCommandLine (cmdLine);
            VerifyTestResult (l.size () == 3);
            VerifyTestResult (l[0] == L"/bin/sh");
            VerifyTestResult (l[1] == L"-c");
            VerifyTestResult (l[2] == L"a b c");
        }
        {
            String           cmdLine = L"";
            Sequence<String> l       = ParseCommandLine (cmdLine);
            VerifyTestResult (l.size () == 0);
        }
        {
            String           cmdLine = L"/bin/sh -c \'a b c\'";
            Sequence<String> l       = ParseCommandLine (cmdLine);
            VerifyTestResult (l.size () == 3);
            VerifyTestResult (l[0] == L"/bin/sh");
            VerifyTestResult (l[1] == L"-c");
            VerifyTestResult (l[2] == L"a b c");
        }
        {
            String           cmdLine = L"/bin/sh\t b c     -d";
            Sequence<String> l       = ParseCommandLine (cmdLine);
            VerifyTestResult (l.size () == 4);
            VerifyTestResult (l[0] == L"/bin/sh");
            VerifyTestResult (l[1] == L"b");
            VerifyTestResult (l[2] == L"c");
            VerifyTestResult (l[3] == L"-d");
        }
    }
}

namespace {
    namespace Test3_ {
        void DoAll ()
        {
            {
                unsigned int cnt = 0;
                {
                    [[maybe_unused]] auto&& c = Finally (
                        [&cnt] () noexcept {
                            cnt--;
                        });
                    cnt++;
                }
                VerifyTestResult (cnt == 0);
            }
        }
    }
}

namespace {
    namespace Test4_VirtualConstant_ {
        namespace Private_ {
            namespace T1_ {
                static const String                      x{L"3"};
                const Execution::VirtualConstant<String> kX = [] () { return x; };
                void                                     DoIt ()
                {
                    const String a = kX;
                }
            }
            namespace T2_ {
                const Execution::VirtualConstant<String> kX = [] () { return L"6"; };
                void                                     DoIt ()
                {
                    const String a = kX;
                }
            }
            namespace T3_ {
                // @todo get constexpr working - see docs for VirtualConstant
                //constexpr Execution::VirtualConstant<int> kX = [] () { return 3; };
                const Execution::VirtualConstant<int> kX = [] () { return 3; };
                void                                  DoIt ()
                {
                    const int [[maybe_unused]] a = kX;
                }
            }
        }
        void DoAll ()
        {
            Private_::T1_::DoIt ();
            Private_::T2_::DoIt ();
            Private_::T3_::DoIt ();
        }
    }
}

namespace {
    namespace Test5_ModuleGetterSetter_ {
        namespace PRIVATE_ {
            using namespace DataExchange;
            using namespace Execution;
            using namespace Time;
            static const Duration kMinTime_ = 1s;
            struct MyData_ {
                bool               fEnabled = false;
                optional<DateTime> fLastSynchronizedAt;
            };
            struct ModuleGetterSetter_Implementation_MyData_ {
                ModuleGetterSetter_Implementation_MyData_ ()
                    : fOptionsFile_{
                          L"MyModule",
                          [] () -> ObjectVariantMapper {
                              ObjectVariantMapper mapper;
                              mapper.AddClass<MyData_> (initializer_list<ObjectVariantMapper::StructFieldInfo>{
                                  {L"Enabled", Stroika_Foundation_DataExchange_StructFieldMetaInfo (MyData_, fEnabled)},
                                  {L"Last-Synchronized-At", Stroika_Foundation_DataExchange_StructFieldMetaInfo (MyData_, fLastSynchronizedAt)},
                              });
                              return mapper;
                          }(),
                          OptionsFile::kDefaultUpgrader, OptionsFile::mkFilenameMapper (L"Put-Your-App-Name-Here")}
                    , fActualCurrentConfigData_ (fOptionsFile_.Read<MyData_> (MyData_ ()))
                {
                    Set (fActualCurrentConfigData_); // assure derived data (and changed fields etc) up to date
                }
                MyData_ Get () const
                {
                    return fActualCurrentConfigData_;
                }
                void Set (const MyData_& v)
                {
                    fActualCurrentConfigData_ = v;
                    fOptionsFile_.Write (v);
                }

            private:
                OptionsFile fOptionsFile_;
                MyData_     fActualCurrentConfigData_; // automatically initialized just in time, and externally synchronized
            };

            using Execution::ModuleGetterSetter;
            ModuleGetterSetter<MyData_, ModuleGetterSetter_Implementation_MyData_> sModuleConfiguration_;

            void TestUse1_ ()
            {
                if (sModuleConfiguration_.Get ().fEnabled) {
                    auto n     = sModuleConfiguration_.Get ();
                    n.fEnabled = false;
                    sModuleConfiguration_.Set (n);
                }
            }
            void TestUse2_ ()
            {
                sModuleConfiguration_.Update ([] (MyData_ data) { MyData_ result = data; if (result.fLastSynchronizedAt.has_value () and *result.fLastSynchronizedAt + kMinTime_ > DateTime::Now ()) { result.fLastSynchronizedAt = DateTime::Now (); } return result; });
            }
            void TestUse3_ ()
            {
                if (sModuleConfiguration_.Update ([] (const MyData_& data) -> optional<MyData_> {  if (data.fLastSynchronizedAt.has_value () and *data.fLastSynchronizedAt + kMinTime_ > DateTime::Now ()) { MyData_ result = data; result.fLastSynchronizedAt = DateTime::Now (); return result; } return {}; })) {
                    // e.g. trigger someone to wakeup and used changes?
                }
            }
        }
        void DoAll ()
        {
            PRIVATE_::TestUse1_ ();
            PRIVATE_::TestUse2_ ();
            PRIVATE_::TestUse3_ ();
        }
    }
}

namespace {
    void DoRegressionTests_ ()
    {
        Test1_Function_ ();
        Test2_CommandLine_ ();
        Test3_::DoAll ();
        Test4_VirtualConstant_::DoAll ();
        Test5_ModuleGetterSetter_::DoAll ();
    }
}

int main ([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[])
{
    Stroika::TestHarness::Setup ();
    return Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
}
<commit_msg>clang++ pickier about where to put [[maybe_unused]]<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2020.  All rights reserved
 */
//  TEST    Foundation::Execution::Other
#include "Stroika/Foundation/StroikaPreComp.h"

#include "Stroika/Foundation/DataExchange/ObjectVariantMapper.h"
#include "Stroika/Foundation/DataExchange/OptionsFile.h"
#include "Stroika/Foundation/Debug/Assertions.h"
#include "Stroika/Foundation/Debug/Trace.h"
#include "Stroika/Foundation/Execution/CommandLine.h"
#include "Stroika/Foundation/Execution/Finally.h"
#include "Stroika/Foundation/Execution/Function.h"
#include "Stroika/Foundation/Execution/ModuleGetterSetter.h"
#include "Stroika/Foundation/Execution/VirtualConstant.h"
#include "Stroika/Foundation/Time/DateTime.h"
#include "Stroika/Foundation/Time/Duration.h"

#include "../TestHarness/SimpleClass.h"
#include "../TestHarness/TestHarness.h"

using namespace Stroika;
using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Execution;

// must be tested before main, so cannot call directly below
namespace {
    int        TestAtomicInitializedCoorectly_ ();
    static int sIgnoredTestValue_ = TestAtomicInitializedCoorectly_ (); // if using static constructors, this will be called before sAtomicBoolNotInitializedTilAfterStaticInitizers_

    atomic<bool> sAtomicBoolNotInitializedTilAfterStaticInitizers_{true}; // for calls before start of or after end of main ()
    int          TestAtomicInitializedCoorectly_ ()
    {
#if qCompilerAndStdLib_atomic_bool_initialize_before_main_Buggy
        if (sAtomicBoolNotInitializedTilAfterStaticInitizers_) {
#if qDebug
            // bug seems to happen just with DEBUG builds - haven't dug into why
            Stroika::TestHarness::WarnTestIssue ("qCompilerAndStdLib_atomic_bool_initialize_before_main_Buggy MAYBE fixed");
#endif
        }
#else
        VerifyTestResult (sAtomicBoolNotInitializedTilAfterStaticInitizers_);
#endif
        return 1;
    }
}

namespace {
    void Test1_Function_ ()
    {
        // Make sure Function<> works as well as std::function
        {
            Function<int (bool)> f = [] ([[maybe_unused]] bool b) -> int { return 3; };
            VerifyTestResult (f (true) == 3);
            function<int (bool)> ff = f;
            VerifyTestResult (ff (true) == 3);
        }
        // Make sure Function<> serves its one purpose - being comparable
        {
            Function<int (bool)> f1 = [] ([[maybe_unused]] bool b) -> int { return 3; };
            Function<int (bool)> f2 = [] ([[maybe_unused]] bool b) -> int { return 3; };

            VerifyTestResult (f1 != f2);
            VerifyTestResult (f1 < f2 or f2 < f1);
            Function<int (bool)> f3 = f1;
            VerifyTestResult (f3 == f1);
            VerifyTestResult (f3 != f2);
        }
    }
}

namespace {
    void Test2_CommandLine_ ()
    {
        {
            String           cmdLine = L"/bin/sh -c \"a b c\"";
            Sequence<String> l       = ParseCommandLine (cmdLine);
            VerifyTestResult (l.size () == 3);
            VerifyTestResult (l[0] == L"/bin/sh");
            VerifyTestResult (l[1] == L"-c");
            VerifyTestResult (l[2] == L"a b c");
        }
        {
            String           cmdLine = L"";
            Sequence<String> l       = ParseCommandLine (cmdLine);
            VerifyTestResult (l.size () == 0);
        }
        {
            String           cmdLine = L"/bin/sh -c \'a b c\'";
            Sequence<String> l       = ParseCommandLine (cmdLine);
            VerifyTestResult (l.size () == 3);
            VerifyTestResult (l[0] == L"/bin/sh");
            VerifyTestResult (l[1] == L"-c");
            VerifyTestResult (l[2] == L"a b c");
        }
        {
            String           cmdLine = L"/bin/sh\t b c     -d";
            Sequence<String> l       = ParseCommandLine (cmdLine);
            VerifyTestResult (l.size () == 4);
            VerifyTestResult (l[0] == L"/bin/sh");
            VerifyTestResult (l[1] == L"b");
            VerifyTestResult (l[2] == L"c");
            VerifyTestResult (l[3] == L"-d");
        }
    }
}

namespace {
    namespace Test3_ {
        void DoAll ()
        {
            {
                unsigned int cnt = 0;
                {
                    [[maybe_unused]] auto&& c = Finally (
                        [&cnt] () noexcept {
                            cnt--;
                        });
                    cnt++;
                }
                VerifyTestResult (cnt == 0);
            }
        }
    }
}

namespace {
    namespace Test4_VirtualConstant_ {
        namespace Private_ {
            namespace T1_ {
                static const String                      x{L"3"};
                const Execution::VirtualConstant<String> kX = [] () { return x; };
                void                                     DoIt ()
                {
                    const String a = kX;
                }
            }
            namespace T2_ {
                const Execution::VirtualConstant<String> kX = [] () { return L"6"; };
                void                                     DoIt ()
                {
                    const String a = kX;
                }
            }
            namespace T3_ {
                // @todo get constexpr working - see docs for VirtualConstant
                //constexpr Execution::VirtualConstant<int> kX = [] () { return 3; };
                const Execution::VirtualConstant<int> kX = [] () { return 3; };
                void                                  DoIt ()
                {
                    const int a [[maybe_unused]] = kX;
                }
            }
        }
        void DoAll ()
        {
            Private_::T1_::DoIt ();
            Private_::T2_::DoIt ();
            Private_::T3_::DoIt ();
        }
    }
}

namespace {
    namespace Test5_ModuleGetterSetter_ {
        namespace PRIVATE_ {
            using namespace DataExchange;
            using namespace Execution;
            using namespace Time;
            static const Duration kMinTime_ = 1s;
            struct MyData_ {
                bool               fEnabled = false;
                optional<DateTime> fLastSynchronizedAt;
            };
            struct ModuleGetterSetter_Implementation_MyData_ {
                ModuleGetterSetter_Implementation_MyData_ ()
                    : fOptionsFile_{
                          L"MyModule",
                          [] () -> ObjectVariantMapper {
                              ObjectVariantMapper mapper;
                              mapper.AddClass<MyData_> (initializer_list<ObjectVariantMapper::StructFieldInfo>{
                                  {L"Enabled", Stroika_Foundation_DataExchange_StructFieldMetaInfo (MyData_, fEnabled)},
                                  {L"Last-Synchronized-At", Stroika_Foundation_DataExchange_StructFieldMetaInfo (MyData_, fLastSynchronizedAt)},
                              });
                              return mapper;
                          }(),
                          OptionsFile::kDefaultUpgrader, OptionsFile::mkFilenameMapper (L"Put-Your-App-Name-Here")}
                    , fActualCurrentConfigData_ (fOptionsFile_.Read<MyData_> (MyData_ ()))
                {
                    Set (fActualCurrentConfigData_); // assure derived data (and changed fields etc) up to date
                }
                MyData_ Get () const
                {
                    return fActualCurrentConfigData_;
                }
                void Set (const MyData_& v)
                {
                    fActualCurrentConfigData_ = v;
                    fOptionsFile_.Write (v);
                }

            private:
                OptionsFile fOptionsFile_;
                MyData_     fActualCurrentConfigData_; // automatically initialized just in time, and externally synchronized
            };

            using Execution::ModuleGetterSetter;
            ModuleGetterSetter<MyData_, ModuleGetterSetter_Implementation_MyData_> sModuleConfiguration_;

            void TestUse1_ ()
            {
                if (sModuleConfiguration_.Get ().fEnabled) {
                    auto n     = sModuleConfiguration_.Get ();
                    n.fEnabled = false;
                    sModuleConfiguration_.Set (n);
                }
            }
            void TestUse2_ ()
            {
                sModuleConfiguration_.Update ([] (MyData_ data) { MyData_ result = data; if (result.fLastSynchronizedAt.has_value () and *result.fLastSynchronizedAt + kMinTime_ > DateTime::Now ()) { result.fLastSynchronizedAt = DateTime::Now (); } return result; });
            }
            void TestUse3_ ()
            {
                if (sModuleConfiguration_.Update ([] (const MyData_& data) -> optional<MyData_> {  if (data.fLastSynchronizedAt.has_value () and *data.fLastSynchronizedAt + kMinTime_ > DateTime::Now ()) { MyData_ result = data; result.fLastSynchronizedAt = DateTime::Now (); return result; } return {}; })) {
                    // e.g. trigger someone to wakeup and used changes?
                }
            }
        }
        void DoAll ()
        {
            PRIVATE_::TestUse1_ ();
            PRIVATE_::TestUse2_ ();
            PRIVATE_::TestUse3_ ();
        }
    }
}

namespace {
    void DoRegressionTests_ ()
    {
        Test1_Function_ ();
        Test2_CommandLine_ ();
        Test3_::DoAll ();
        Test4_VirtualConstant_::DoAll ();
        Test5_ModuleGetterSetter_::DoAll ();
    }
}

int main ([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[])
{
    Stroika::TestHarness::Setup ();
    return Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 Egor Pugin <egor.pugin@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <primitives/date_time.h>

TimePoint getUtc()
{
    boost::posix_time::ptime t(
        boost::gregorian::day_clock::universal_day(),
        boost::posix_time::second_clock::universal_time().time_of_day());
    return Clock::from_time_t(boost::posix_time::to_time_t(t));
}

TimePoint string2timepoint(const String &s)
{
    auto t = boost::posix_time::time_from_string(s);
    return Clock::from_time_t(boost::posix_time::to_time_t(t));
}

String timepoint2string(const TimePoint &t)
{
    auto t2 = boost::posix_time::from_time_t(Clock::to_time_t(t));
    return boost::posix_time::to_simple_string(t2);
}

time_t string2time_t(const String &s)
{
    return Clock::to_time_t(string2timepoint(s));
}

String local_time()
{
    auto t = boost::posix_time::second_clock::local_time();
    return boost::posix_time::to_simple_string(t);
}
<commit_msg>Fix reading timepoints.<commit_after>// Copyright (C) 2018 Egor Pugin <egor.pugin@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <primitives/date_time.h>

TimePoint getUtc()
{
    boost::posix_time::ptime t(
        boost::gregorian::day_clock::universal_day(),
        boost::posix_time::second_clock::universal_time().time_of_day());
    return Clock::from_time_t(boost::posix_time::to_time_t(t));
}

TimePoint string2timepoint(const String &s)
{
    boost::posix_time::ptime t;
    std::istringstream ss(s);
    ss >> t;
    return Clock::from_time_t(boost::posix_time::to_time_t(t));
}

String timepoint2string(const TimePoint &t)
{
    auto t2 = boost::posix_time::from_time_t(Clock::to_time_t(t));
    return boost::posix_time::to_simple_string(t2);
}

time_t string2time_t(const String &s)
{
    return Clock::to_time_t(string2timepoint(s));
}

String local_time()
{
    auto t = boost::posix_time::second_clock::local_time();
    return boost::posix_time::to_simple_string(t);
}
<|endoftext|>


<commit_before>#include <debugger/bridge/delayload.h>
#include <debugger/lua.h>
#include <debugger/debugger.h>
#include <debugger/io/socket_impl.h>
#include <bee/utility/unicode.h>
#include <bee/net/endpoint.h>
#include <memory>
#include <string_view>

#if defined(DEBUGGER_BRIDGE)
#include <intrin.h>
#endif

static int DBG = 0;

static std::string_view luaL_checkstrview(lua_State* L, int idx) {
	size_t len = 0;
	const char* str = luaL_checklstring(L, idx, &len);
	return std::string_view(str, len);
}

namespace luaw {
	struct ud {
		std::unique_ptr<vscode::io::socket_s> socket_s;
		std::unique_ptr<vscode::io::socket_c> socket_c;
		std::unique_ptr<vscode::debugger> dbg;
		bool guard = false;

		std::pair<std::string, uint16_t> split_address(const std::string& addr) {

			size_t pos = addr.find(':');
			if (pos == addr.npos) {
				return { addr, 0 };
			}
			else {
				return { addr.substr(0, pos), atoi(addr.substr(pos + 1).c_str()) };
			}
		}
		void listen_tcp(const char* addr)
		{
			if (dbg) return;
			auto [ip, port] = split_address(addr);
			auto info = bee::net::endpoint::from_hostname(ip, port);
			if (!info) {
				return;
			}
			socket_s.reset(new vscode::io::socket_s(info.value()));
			dbg.reset(new vscode::debugger(socket_s.get()));
		}

		void connect_tcp(const char* addr)
		{
			if (dbg) return;
			auto[ip, port] = split_address(addr);
			auto info = bee::net::endpoint::from_hostname(ip, port);
			if (!info) {
				return;
			}
			socket_c.reset(new vscode::io::socket_c(info.value()));
			dbg.reset(new vscode::debugger(socket_c.get()));
		}
		void listen_pipe(const char* path)
		{
			if (dbg) return;
			auto info = bee::net::endpoint::from_unixpath(path);
			if (!info) {
				return;
			}
			auto[upath, uport] = info->info();
			::DeleteFileW(bee::u2w(upath).c_str());
			socket_s.reset(new vscode::io::socket_s(info.value()));
			dbg.reset(new vscode::debugger(socket_s.get()));
		}
	};

	static std::unique_ptr<ud> global;

	static ud& get()
	{
		if (!global) {
			global.reset(new ud);
		}
		return *global;
	}

	static int constructor(lua_State* L)
	{
		lua_newuserdata(L, 1);
		luaL_setmetatable(L, "vscode::debugger");
		return 1;
	}

	static int io(lua_State* L)
	{
		ud& self = get();
		const char* addr = luaL_checkstring(L, 2);
		if (strncmp(addr, "listen:", 7) == 0) {
			self.listen_tcp(addr + 7);
		}
		else if (strncmp(addr, "connect:", 8) == 0) {
			self.connect_tcp(addr + 8);
		}
		else if (strncmp(addr, "pipe:", 5) == 0) {
			self.listen_pipe(addr + 5);
		}
		else {
			self.listen_tcp(addr);
		}
		lua_pushvalue(L, 1);
		return 1;
	}

	static int wait(lua_State* L)
	{
		ud& self = get();
		if (!self.dbg) {
			lua_pushvalue(L, 1);
			return 1;
		}
		self.dbg->wait_client();
		lua_pushvalue(L, 1);
		return 1;
	}

	static int start(lua_State* L)
	{
		ud& self = get();
		if (!self.dbg) {
			lua_pushvalue(L, 1);
			return 1;
		}
		self.dbg->attach_lua(L);
		lua_pushvalue(L, 1);
		return 1;
	}

	static int config(lua_State* L)
	{
		ud& self = get();
		if (!self.dbg) {
			lua_pushvalue(L, 1);
			return 1;
		}
		if (lua_type(L, 2) == LUA_TSTRING) {
			size_t len = 0;
			const char* str = luaL_checklstring(L, 2, &len);
			std::string err = "unknown";
			if (!self.dbg->set_config(0, std::string(str, len), err)) {
				lua_pushlstring(L, err.data(), err.size());
				return lua_error(L);
			}
		}
		if (lua_type(L, 3) == LUA_TSTRING) {
			size_t len = 0;
			const char* str = luaL_checklstring(L, 3, &len);
			std::string err = "unknown";
			if (!self.dbg->set_config(2, std::string(str, len), err)) {
				lua_pushlstring(L, err.data(), err.size());
				return lua_error(L);
			}
		}
		lua_pushvalue(L, 1);
		return 1;
	}

	static int redirect(lua_State* L)
	{
		ud& self = get();
		if (!self.dbg) {
			lua_pushvalue(L, 1);
			return 1;
		}
		if (lua_type(L, 2) == LUA_TSTRING) {
			const char* type = luaL_checkstring(L, 2);
			if (strcmp(type, "stdout") == 0) {
				self.dbg->open_redirect(vscode::eRedirect::stdoutput);
			}
			else if (strcmp(type, "stderr") == 0) {
				self.dbg->open_redirect(vscode::eRedirect::stderror);
			}
			else if (strcmp(type, "print") == 0) {
				self.dbg->open_redirect(vscode::eRedirect::print, L);
			}
		}
		lua_pushvalue(L, 1);
		return 1;
	}

	static int guard(lua_State* L)
	{
		ud& self = get();
		self.guard = true;
		lua_pushvalue(L, 1);
		return 1;
	}

	static int exception(lua_State* L)
	{
		ud& self = get();
		std::string_view type_str = luaL_checkstrview(L, 2);
		vscode::eException type;
		if (type_str == "pcall") {
			type = vscode::eException::pcall;
		}
		else if (type_str == "xpcall") {
			type = vscode::eException::xpcall;
		}
		else {
			return luaL_error(L, "Unknown exception type: %s.", type_str.data());
		}
		luaL_checktype(L, 3, LUA_TSTRING);
		lua_pushvalue(L, 3);
		self.dbg->exception(L, type, (int)luaL_checkinteger(L, 4));
		return 0;
	}

	static int event(lua_State* L)
	{
		ud& self = get();
		self.dbg->event(luaL_checkstring(L, 2), L, 3, lua_gettop(L));
		return 0;
	}

	static int mt_gc(lua_State* L)
	{
		ud& self = get();
		if (self.dbg) {
			self.dbg->detach_lua(L);
		}
		if (self.guard) {
			self.dbg.reset();
			self.socket_s.reset();
			self.socket_c.reset();
		}
		return 0;
	}

	int open(lua_State* L)
	{
		luaL_Reg mt[] = {
			{ "io", io },
			{ "wait", wait },
			{ "start", start },
			{ "config", config },
			{ "redirect", redirect },
			{ "guard", guard },
			{ "event", event },
			{ "exception", exception },
			{ "__gc", mt_gc },
			{ NULL, NULL },
		};
		luaL_newmetatable(L, "vscode::debugger");
		luaL_setfuncs(L, mt, 0);
		lua_pushvalue(L, -1);
		lua_setfield(L, -2, "__index");
		lua_pop(L, 1);
		return constructor(L);
	}
}

#if defined(_WIN32)
void debugger_create(const char* path)
{
	luaw::get().listen_pipe(path);
}
vscode::debugger* debugger_get()
{
	return luaw::get().dbg.get();
}
#endif

#if defined(DEBUGGER_BRIDGE)
static void caller_is_luadll(void* callerAddress)
{
	HMODULE  caller = NULL;
	if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)callerAddress, &caller) && caller)
	{
		if (GetProcAddress(caller, "lua_newstate"))
		{
			delayload::set_luadll(caller, ::GetProcAddress);
		}
	}
}
#endif

int luaopen_debugger(lua_State* L)
{
#if defined(DEBUGGER_BRIDGE)
	caller_is_luadll(_ReturnAddress());
#endif
	if (lua_rawgetp(L, LUA_REGISTRYINDEX, &DBG) != LUA_TNIL) {
		return 1;
	}
	lua_pop(L, 1);

	luaw::open(L);
	lua_pushvalue(L, -1);
	lua_rawsetp(L, LUA_REGISTRYINDEX, &DBG);
	return 1;
}
<commit_msg>移除connect<commit_after>#include <debugger/bridge/delayload.h>
#include <debugger/lua.h>
#include <debugger/debugger.h>
#include <debugger/io/socket_impl.h>
#include <bee/utility/unicode.h>
#include <bee/net/endpoint.h>
#include <memory>
#include <string_view>

#if defined(DEBUGGER_BRIDGE)
#include <intrin.h>
#endif

static int DBG = 0;

static std::string_view luaL_checkstrview(lua_State* L, int idx) {
	size_t len = 0;
	const char* str = luaL_checklstring(L, idx, &len);
	return std::string_view(str, len);
}

namespace luaw {
	struct ud {
		std::unique_ptr<vscode::io::socket_s> socket_s;
		std::unique_ptr<vscode::debugger> dbg;
		bool guard = false;

		std::pair<std::string, uint16_t> split_address(const std::string& addr) {

			size_t pos = addr.find(':');
			if (pos == addr.npos) {
				return { addr, 0 };
			}
			else {
				return { addr.substr(0, pos), atoi(addr.substr(pos + 1).c_str()) };
			}
		}
		void listen_tcp(const char* addr)
		{
			if (dbg) return;
			auto [ip, port] = split_address(addr);
			auto info = bee::net::endpoint::from_hostname(ip, port);
			if (!info) {
				return;
			}
			socket_s.reset(new vscode::io::socket_s(info.value()));
			dbg.reset(new vscode::debugger(socket_s.get()));
		}
		void listen_pipe(const char* path)
		{
			if (dbg) return;
			auto info = bee::net::endpoint::from_unixpath(path);
			if (!info) {
				return;
			}
			auto[upath, uport] = info->info();
			::DeleteFileW(bee::u2w(upath).c_str());
			socket_s.reset(new vscode::io::socket_s(info.value()));
			dbg.reset(new vscode::debugger(socket_s.get()));
		}
	};

	static std::unique_ptr<ud> global;

	static ud& get()
	{
		if (!global) {
			global.reset(new ud);
		}
		return *global;
	}

	static int constructor(lua_State* L)
	{
		lua_newuserdata(L, 1);
		luaL_setmetatable(L, "vscode::debugger");
		return 1;
	}

	static int io(lua_State* L)
	{
		ud& self = get();
		const char* addr = luaL_checkstring(L, 2);
		if (strncmp(addr, "listen:", 7) == 0) {
			self.listen_tcp(addr + 7);
		}
		else if (strncmp(addr, "pipe:", 5) == 0) {
			self.listen_pipe(addr + 5);
		}
		else {
			self.listen_tcp(addr);
		}
		lua_pushvalue(L, 1);
		return 1;
	}

	static int wait(lua_State* L)
	{
		ud& self = get();
		if (!self.dbg) {
			lua_pushvalue(L, 1);
			return 1;
		}
		self.dbg->wait_client();
		lua_pushvalue(L, 1);
		return 1;
	}

	static int start(lua_State* L)
	{
		ud& self = get();
		if (!self.dbg) {
			lua_pushvalue(L, 1);
			return 1;
		}
		self.dbg->attach_lua(L);
		lua_pushvalue(L, 1);
		return 1;
	}

	static int config(lua_State* L)
	{
		ud& self = get();
		if (!self.dbg) {
			lua_pushvalue(L, 1);
			return 1;
		}
		if (lua_type(L, 2) == LUA_TSTRING) {
			size_t len = 0;
			const char* str = luaL_checklstring(L, 2, &len);
			std::string err = "unknown";
			if (!self.dbg->set_config(0, std::string(str, len), err)) {
				lua_pushlstring(L, err.data(), err.size());
				return lua_error(L);
			}
		}
		if (lua_type(L, 3) == LUA_TSTRING) {
			size_t len = 0;
			const char* str = luaL_checklstring(L, 3, &len);
			std::string err = "unknown";
			if (!self.dbg->set_config(2, std::string(str, len), err)) {
				lua_pushlstring(L, err.data(), err.size());
				return lua_error(L);
			}
		}
		lua_pushvalue(L, 1);
		return 1;
	}

	static int redirect(lua_State* L)
	{
		ud& self = get();
		if (!self.dbg) {
			lua_pushvalue(L, 1);
			return 1;
		}
		if (lua_type(L, 2) == LUA_TSTRING) {
			const char* type = luaL_checkstring(L, 2);
			if (strcmp(type, "stdout") == 0) {
				self.dbg->open_redirect(vscode::eRedirect::stdoutput);
			}
			else if (strcmp(type, "stderr") == 0) {
				self.dbg->open_redirect(vscode::eRedirect::stderror);
			}
			else if (strcmp(type, "print") == 0) {
				self.dbg->open_redirect(vscode::eRedirect::print, L);
			}
		}
		lua_pushvalue(L, 1);
		return 1;
	}

	static int guard(lua_State* L)
	{
		ud& self = get();
		self.guard = true;
		lua_pushvalue(L, 1);
		return 1;
	}

	static int exception(lua_State* L)
	{
		ud& self = get();
		std::string_view type_str = luaL_checkstrview(L, 2);
		vscode::eException type;
		if (type_str == "pcall") {
			type = vscode::eException::pcall;
		}
		else if (type_str == "xpcall") {
			type = vscode::eException::xpcall;
		}
		else {
			return luaL_error(L, "Unknown exception type: %s.", type_str.data());
		}
		luaL_checktype(L, 3, LUA_TSTRING);
		lua_pushvalue(L, 3);
		self.dbg->exception(L, type, (int)luaL_checkinteger(L, 4));
		return 0;
	}

	static int event(lua_State* L)
	{
		ud& self = get();
		self.dbg->event(luaL_checkstring(L, 2), L, 3, lua_gettop(L));
		return 0;
	}

	static int mt_gc(lua_State* L)
	{
		ud& self = get();
		if (self.dbg) {
			self.dbg->detach_lua(L);
		}
		if (self.guard) {
			self.dbg.reset();
			self.socket_s.reset();
		}
		return 0;
	}

	int open(lua_State* L)
	{
		luaL_Reg mt[] = {
			{ "io", io },
			{ "wait", wait },
			{ "start", start },
			{ "config", config },
			{ "redirect", redirect },
			{ "guard", guard },
			{ "event", event },
			{ "exception", exception },
			{ "__gc", mt_gc },
			{ NULL, NULL },
		};
		luaL_newmetatable(L, "vscode::debugger");
		luaL_setfuncs(L, mt, 0);
		lua_pushvalue(L, -1);
		lua_setfield(L, -2, "__index");
		lua_pop(L, 1);
		return constructor(L);
	}
}

#if defined(_WIN32)
void debugger_create(const char* path)
{
	luaw::get().listen_pipe(path);
}
vscode::debugger* debugger_get()
{
	return luaw::get().dbg.get();
}
#endif

#if defined(DEBUGGER_BRIDGE)
static void caller_is_luadll(void* callerAddress)
{
	HMODULE  caller = NULL;
	if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)callerAddress, &caller) && caller)
	{
		if (GetProcAddress(caller, "lua_newstate"))
		{
			delayload::set_luadll(caller, ::GetProcAddress);
		}
	}
}
#endif

int luaopen_debugger(lua_State* L)
{
#if defined(DEBUGGER_BRIDGE)
	caller_is_luadll(_ReturnAddress());
#endif
	if (lua_rawgetp(L, LUA_REGISTRYINDEX, &DBG) != LUA_TNIL) {
		return 1;
	}
	lua_pop(L, 1);

	luaw::open(L);
	lua_pushvalue(L, -1);
	lua_rawsetp(L, LUA_REGISTRYINDEX, &DBG);
	return 1;
}
<|endoftext|>