0027620: Test perf bop boxholes crashes DRAW IR-2018-11-23 IR-WEEK47
authorabv <abv@opencascade.com>
Sat, 17 Nov 2018 09:51:26 +0000 (12:51 +0300)
committerapn <apn@opencascade.com>
Fri, 23 Nov 2018 09:18:42 +0000 (12:18 +0300)
Implementation of capturing of output to standard streams in DRAW (see command dlog) is revised to avoid problems with command "test" executing long test scripts:

1. Method OSD_File::Capture() is removed: on Windows it was allocating a C file descriptor for a file opened using WinAPI, and never released that descriptor (once allocated, it cannot be released separately from WinAPI file handle). Direct calls to dup/dup2 are used instead.

2. In Draw_Window.cxx the standard Tcl channels are initialized manually using corrected version of Tcl internal function. This works around a problem with Tcl channels on Windows being bound to OS device handle owned by the system which can get invalidated as result of calls to dup2() (used to capture output to standard streams).

3. Temporary file for capturing is opened once and used to store whole log, thus the need to collect log in the string stream in memory is avoided

4. Possible errors of dup() and dup2() are checked and reported

Test demo draw dlog is added

Off-topic changes:

- Test demo draw getsource is corrected for VS2017 which generates file name in lowercase
- Field myFaceBounds is initialized in constructor of the class BRepAlgo_NormalProjection to avoid undefined behavior
- Test bugs modalg_5 bug24012 is corrected to use command nproject instead of custom one, and to check propertes of the resulting shape

src/BRepAlgo/BRepAlgo_NormalProjection.cxx
src/Draw/Draw_BasicCommands.cxx
src/Draw/Draw_Interpretor.cxx
src/Draw/Draw_Interpretor.hxx
src/Draw/Draw_Window.cxx
src/OSD/OSD_File.cxx
src/OSD/OSD_File.hxx
src/QABugs/QABugs_19.cxx
tests/bugs/modalg_5/bug24012
tests/demo/draw/dlog [new file with mode: 0644]
tests/demo/draw/getsource

index 5910205..434ebf4 100644 (file)
@@ -83,14 +83,12 @@ void ResultChron( OSD_Chronometer & ch, Standard_Real & time)
 //=======================================================================
 
  BRepAlgo_NormalProjection::BRepAlgo_NormalProjection() 
-   : myWith3d(Standard_True)
-
+   : myIsDone(Standard_False), myMaxDist(-1.),
+     myWith3d(Standard_True), myFaceBounds(Standard_True)
 {
   BRep_Builder BB;
   BB.MakeCompound(TopoDS::Compound(myToProj));
-  myFaceBounds=Standard_True;
   SetDefaultParams();
-  myMaxDist = -1;
 }
 
 //=======================================================================
@@ -99,12 +97,12 @@ void ResultChron( OSD_Chronometer & ch, Standard_Real & time)
 //=======================================================================
 
  BRepAlgo_NormalProjection::BRepAlgo_NormalProjection(const TopoDS_Shape& S) 
-    : myWith3d(Standard_True)
+   : myIsDone(Standard_False), myMaxDist(-1.),
+     myWith3d(Standard_True), myFaceBounds(Standard_True)
 {
   BRep_Builder BB;
   BB.MakeCompound(TopoDS::Compound(myToProj));
   SetDefaultParams();
-  myMaxDist = -1;
   Init(S);
 }
 
index bf67e02..a30799e 100644 (file)
@@ -273,15 +273,20 @@ static Standard_Integer dlog(Draw_Interpretor& di, Standard_Integer n, const cha
   }
   else if (! strcmp (a[1], "reset") && n == 2)
   {
-    di.Log().str("");
+    di.ResetLog();
   }
   else if (! strcmp (a[1], "get") && n == 2)
   {
-    di << di.Log().str().c_str();
+    di << di.GetLog();
   }
   else if (! strcmp (a[1], "add") && n == 3)
   {
-    di.Log() << a[2] << "\n";
+    di.AddLog (a[2]);
+    di.AddLog ("\n");
+  }
+  else if (! strcmp (a[1], "status") && n == 2)
+  {
+    di << (di.GetDoLog() ? "on" : "off");
   }
   else {
     cout << "Unrecognized option(s): " << a[1] << endl;
index 025df31..411fbf4 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <string.h>
 #include <tcl.h>
+#include <fcntl.h>
 #ifndef _WIN32
 #include <unistd.h>
 #endif
@@ -37,6 +38,7 @@
 // for capturing of cout and cerr (dup(), dup2())
 #ifdef _WIN32
 #include <io.h>
+#include <sys/stat.h>  
 #endif
 
 #if ! defined(STDOUT_FILENO)
@@ -67,50 +69,45 @@ namespace {
     cout << flush;
   }
 
-  int capture_start (OSD_File& theTmpFile, int std_fd)
+  int capture_start (int theFDStd, int theFDLog)
   {
-    theTmpFile.BuildTemporary();
-    if (theTmpFile.Failed())
+    Standard_ASSERT_RETURN (theFDLog >= 0, "Invalid descriptor of log file", -1);
+
+    // Duplicate a file descriptor of the standard stream to be able to restore output to it later
+    int aFDSave = dup (theFDStd);
+    if (aFDSave < 0)
+    {
+      perror ("Error capturing standard stream to log: dup() returned");
+      return -1;
+    }
+
+    // Redirect the stream to the log file
+    if (dup2 (theFDLog, theFDStd) < 0)
     {
-      cerr << "Error: cannot create temporary file for capturing console output" << endl;
+      close (aFDSave);
+      perror ("Error capturing standard stream to log: dup2() returned");
       return -1;
     }
 
-    // remember current file descriptors of standard stream, and replace it by temporary
-    return theTmpFile.Capture(std_fd);
+    // remember saved file descriptor of standard stream
+    return aFDSave;
   }
 
-  void capture_end (OSD_File* tmp_file, int std_fd, int save_fd, Standard_OStream &log, Standard_Boolean doEcho)
+  void capture_end (int theFDStd, int& theFDSave)
   {
-    if (!tmp_file)
+    if (theFDSave < 0)
       return;
 
     // restore normal descriptors of console stream
-  #ifdef _WIN32
-    _dup2(save_fd, std_fd);
-    _close(save_fd);
-  #else
-    dup2(save_fd, std_fd);
-    close(save_fd);
-  #endif
-
-    // extract all output and copy it to log and optionally to cout
-    const int BUFSIZE = 2048;
-    TCollection_AsciiString buf;
-    tmp_file->Rewind();
-    while (tmp_file->ReadLine (buf, BUFSIZE) > 0)
+    if (dup2(theFDSave, theFDStd) < 0)
     {
-      log << buf;
-      if (doEcho) 
-        cout << buf;
+      perror ("Error returning capturing standard stream to log: dup2() returned");
+      return;
     }
 
-    // close temporary file
-    tmp_file->Close();
-
-    // remove temporary file if this is not done by the system
-    if (tmp_file->Exists())
-      tmp_file->Remove();
+    // close saved file descriptor
+    close(theFDSave);
+    theFDSave = -1;
   }
 
 } // anonymous namespace
@@ -136,24 +133,24 @@ static Standard_Integer CommandCmd
                                         strcmp (argv[0], "decho") == 0);
   Standard_Boolean doLog  = (di.GetDoLog() && ! isLogManipulation);
   Standard_Boolean doEcho = (di.GetDoEcho() && ! isLogManipulation);
-  if (doLog)
-    dumpArgs (di.Log(), argc, argv);
-  if (doEcho)
-    dumpArgs (cout, argc, argv);
 
   // flush cerr and cout
   flush_standard_streams();
 
   // capture cout and cerr to log
-  OSD_File aFile_out, aFile_err;
-  int fd_err_save = -1;
-  int fd_out_save = -1;
+  int aFDstdout = STDOUT_FILENO;
+  int aFDstderr = STDERR_FILENO;
+  int aFDerr_save = -1;
+  int aFDout_save = -1;
   if (doLog)
   {
-    fd_out_save = capture_start (aFile_out, STDOUT_FILENO);
-    fd_err_save = capture_start (aFile_err, STDERR_FILENO);
+    aFDout_save = capture_start (aFDstdout, di.GetLogFileDescriptor());
+    aFDerr_save = capture_start (aFDstderr, di.GetLogFileDescriptor());
   }
 
+  if (doEcho || doLog)
+    dumpArgs (cout, argc, argv);
+
   // run command
   try {
     OCC_CATCH_SIGNALS
@@ -195,30 +192,24 @@ static Standard_Integer CommandCmd
     code = TCL_ERROR;
   }
 
+  // log command result
+  if (doLog || doEcho)
+  {
+    const char* aResultStr = Tcl_GetStringResult (interp);
+    if (aResultStr != 0 && aResultStr[0] != '\0' )
+    {
+      std::cout << aResultStr << std::endl;
+    }
+  }
+
   // flush streams
   flush_standard_streams();
 
   // end capturing cout and cerr 
   if (doLog) 
   {
-    capture_end (&aFile_err, STDERR_FILENO, fd_err_save, di.Log(), doEcho);
-    capture_end (&aFile_out, STDOUT_FILENO, fd_out_save, di.Log(), doEcho);
-  }
-
-  // log command result
-  const char* aResultStr = NULL;
-  if (doLog)
-  {
-    aResultStr = Tcl_GetStringResult (interp);
-    if (aResultStr != 0 && aResultStr[0] != '\0' )
-      di.Log() << Tcl_GetStringResult (interp) << endl;
-  }
-  if (doEcho)
-  {
-    if (aResultStr == NULL)
-      aResultStr = Tcl_GetStringResult (interp);
-    if (aResultStr != 0 && aResultStr[0] != '\0' )
-      cout << Tcl_GetStringResult (interp) << endl;
+    capture_end (aFDstderr, aFDerr_save);
+    capture_end (aFDstdout, aFDout_save);
   }
 
   return code;
@@ -236,7 +227,7 @@ static void CommandDelete (ClientData theClientData)
 //=======================================================================
 
 Draw_Interpretor::Draw_Interpretor() :
-  isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False)
+  isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False), myFDLog(-1)
 {
 // The tcl interpreter is not created immediately as it is kept 
 // by a global variable and created and deleted before the main().
@@ -529,6 +520,13 @@ Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
 
 Draw_Interpretor::~Draw_Interpretor()
 {
+  SetDoLog (Standard_False);
+  if (myFDLog >=0)
+  {
+    close (myFDLog);
+    myFDLog = 0;
+  }
+
   // MKV 01.02.05
 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
   try {
@@ -573,6 +571,35 @@ void Draw_Interpretor::Set(const Draw_PInterp& PIntrp)
 
 void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
 {
+  if (myDoLog == doLog)
+    return;
+
+  // create log file if not opened yet
+  if (doLog && myFDLog < 0)
+  {
+#ifdef _WIN32
+    char tmpfile[L_tmpnam + 1];
+    tmpnam(tmpfile);
+    myFDLog = open (tmpfile, O_RDWR | O_CREAT | O_EXCL | O_TEMPORARY, S_IREAD | S_IWRITE);
+#else
+    // according to Linux Filesystem Hierarchy Standard, 3.17,
+    // /tmp/ is the right directory for temporary files
+    char tmpfile[256] = "/tmp/occt_draw_XXXXXX";
+    myFDLog = mkstemp (tmpfile);
+    if (myFDLog >= 0)
+    {
+//      printf ("Tmp file: %s\n", tmpfile);
+      unlink (tmpfile); // make sure the file will be deleted on close
+    }
+#endif
+    if (myFDLog < 0)
+    {
+      perror ("Error creating temporary file for capturing console output");
+      printf ("path: %s\n", tmpfile);
+      return;
+    }
+  }
+
   myDoLog = doLog;
 }
 
@@ -591,7 +618,67 @@ Standard_Boolean Draw_Interpretor::GetDoEcho () const
   return myDoEcho;
 }
 
-Standard_SStream& Draw_Interpretor::Log ()
+void Draw_Interpretor::ResetLog ()
 {
-  return myLog;
+  if (myFDLog < 0)
+    return;
+
+  // flush cerr and cout, for the case if they are bound to the log
+  flush_standard_streams();
+
+  lseek (myFDLog, 0, SEEK_SET);
+
+#ifdef _WIN32
+  if (_chsize_s (myFDLog, 0) != 0)
+#else
+  if (ftruncate (myFDLog, 0) != 0)
+#endif
+  {
+    perror ("Error truncating the console log");
+  }
+}
+
+void Draw_Interpretor::AddLog (const Standard_CString theStr)
+{
+  if (myFDLog < 0 || ! theStr || ! theStr[0])
+    return;
+
+  // flush cerr and cout, for the case if they are bound to the log
+  flush_standard_streams();
+
+  // write as plain bytes
+  if (write (myFDLog, theStr, (unsigned int)strlen(theStr)) <0)
+  {
+    perror ("Error writing to console log");
+  }
+}
+
+TCollection_AsciiString Draw_Interpretor::GetLog ()
+{
+  TCollection_AsciiString aLog;
+  if (myFDLog < 0)
+    return aLog;
+
+  // flush cerr and cout
+  flush_standard_streams();
+
+  // rewind the file to its start
+  lseek (myFDLog, 0, SEEK_SET);
+
+  // read the whole log to string; this implementation
+  // is not optimized but should be sufficient
+  const int BUFSIZE = 4096;
+  char buffer[BUFSIZE + 1];
+  for (;;)
+  {
+    int nbRead = read (myFDLog, buffer, BUFSIZE);
+    if (nbRead <= 0)
+    {
+      break;
+    }
+    buffer[nbRead] = '\0';
+    aLog.AssignCat (buffer);
+  }
+
+  return aLog;
 }
index 9e113bc..579f6f9 100644 (file)
@@ -238,8 +238,18 @@ public:
   //! Returns true if echoing of commands is enabled
   Standard_EXPORT Standard_Boolean GetDoEcho() const;
 
-  //! Returns log stream
-  Standard_EXPORT Standard_SStream& Log();
+  //! Resets log (if opened) to zero size
+  Standard_EXPORT void ResetLog();
+
+  //! Writes a text string to the log (if opened);
+  //! end of line is not appended
+  Standard_EXPORT void AddLog (const Standard_CString theStr);
+
+  //! Returns current content of the log file as a text string
+  Standard_EXPORT TCollection_AsciiString GetLog();
+
+  //! Returns current value of the log file descriptor
+  Standard_Integer GetLogFileDescriptor() { return myFDLog; }
 
 protected:
 
@@ -255,7 +265,7 @@ private:
   Draw_PInterp     myInterp;
   Standard_Boolean myDoLog;
   Standard_Boolean myDoEcho;
-  Standard_SStream myLog;
+  Standard_Integer myFDLog;          //!< file descriptor of log file 
 
 public:
 
index 93a4606..7aab0be 100644 (file)
@@ -2166,16 +2166,120 @@ void exitProc(ClientData /*dc*/)
   TerminateProcess(proc, 0);
 }
 
+// This is fixed version of TclpGetDefaultStdChannel() defined in tclWinChan.c
+// See https://core.tcl.tk/tcl/tktview/91c9bc1c457fda269ae18595944fc3c2b54d961d
+static Tcl_Channel
+TclpGetDefaultStdChannel(
+    int type)                  /* One of TCL_STDIN, TCL_STDOUT, or
+                                * TCL_STDERR. */
+{
+    Tcl_Channel channel;
+    HANDLE handle;
+    int mode = -1;
+    const char *bufMode = NULL;
+    DWORD handleId = (DWORD) -1;
+                               /* Standard handle to retrieve. */
+
+    switch (type) {
+    case TCL_STDIN:
+       handleId = STD_INPUT_HANDLE;
+       mode = TCL_READABLE;
+       bufMode = "line";
+       break;
+    case TCL_STDOUT:
+       handleId = STD_OUTPUT_HANDLE;
+       mode = TCL_WRITABLE;
+       bufMode = "line";
+       break;
+    case TCL_STDERR:
+       handleId = STD_ERROR_HANDLE;
+       mode = TCL_WRITABLE;
+       bufMode = "none";
+       break;
+    default:
+       Tcl_Panic("TclGetDefaultStdChannel: Unexpected channel type");
+       break;
+    }
+
+    handle = GetStdHandle(handleId);
+
+    /*
+     * Note that we need to check for 0 because Windows may return 0 if this
+     * is not a console mode application, even though this is not a valid
+     * handle.
+     */
+
+    if ((handle == INVALID_HANDLE_VALUE) || (handle == 0)) {
+       return (Tcl_Channel) NULL;
+    }
+
+    /*
+     * Make duplicate of the standard handle as it may be altered
+     * (closed, reopened with another type of the object etc.) by
+     * the system or a user code at any time, e.g. by call to _dup2()
+     */
+    if (! DuplicateHandle (GetCurrentProcess(), handle, 
+                           GetCurrentProcess(), &handle,
+                           0, FALSE, DUPLICATE_SAME_ACCESS)) {
+       return (Tcl_Channel) NULL;
+    }
+
+    channel = Tcl_MakeFileChannel(handle, mode);
+
+    if (channel == NULL) {
+       return (Tcl_Channel) NULL;
+    }
+
+    /*
+     * Set up the normal channel options for stdio handles.
+     */
+
+    if (Tcl_SetChannelOption(NULL,channel,"-translation","auto")!=TCL_OK ||
+           Tcl_SetChannelOption(NULL,channel,"-eofchar","\032 {}")!=TCL_OK ||
+           Tcl_SetChannelOption(NULL,channel,"-buffering",bufMode)!=TCL_OK) {
+       Tcl_Close(NULL, channel);
+       return (Tcl_Channel) NULL;
+    }
+    return channel;
+}
+
+// helper functuion
+static void ResetStdChannel (int type)
+{
+  Tcl_Channel aChannel = TclpGetDefaultStdChannel (type);
+  Tcl_SetStdChannel (aChannel, type);
+  if (aChannel)
+  {
+    Tcl_RegisterChannel (NULL, aChannel);
+  }
+}
+
 /*--------------------------------------------------------*\
 |  tkLoop: implements Tk_Main()-like behaviour in a separate thread
 \*--------------------------------------------------------*/
 static DWORD WINAPI tkLoop(VOID)
 {
-  Draw_Interpretor& aCommands = Draw::GetInterpretor();
-
   Tcl_CreateExitHandler(exitProc, 0);
+  
+  // Work-around against issue with Tcl standard channels on Windows.
+  // These channels by default use OS handles owned by the system which
+  // may get invalidated e.g. by dup2() (see dlog command).
+  // If this happens, output to stdout from Tcl (e.g. puts) gets broken
+  // (sympthom is error message: "error writing "stdout": bad file number").
+  // To prevent this, we set standard channels using duplicate of system handles.
+  // The effect is that Tcl channel becomes independent on C file descriptor
+  // and even if stdout/stderr are redirected using dup2(), Tcl keeps using
+  // original device.
+  ResetStdChannel (TCL_STDOUT);
+  ResetStdChannel (TCL_STDERR);
+
 #if (TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5))
+  // Plain Tcl (8.6.4+) initializes interpretor channels automatically, but 
+  // ActiveState Tcl (at least 8.6.4) does not seem to do that, so channels 
+  // need to be set into interpretor explicitly
   {
+    Draw_Interpretor& aCommands = Draw::GetInterpretor();
+
     Tcl_Channel aChannelIn  = Tcl_GetStdChannel (TCL_STDIN);
     Tcl_Channel aChannelOut = Tcl_GetStdChannel (TCL_STDOUT);
     Tcl_Channel aChannelErr = Tcl_GetStdChannel (TCL_STDERR);
index 4b759ed..cb4850f 100644 (file)
@@ -372,8 +372,8 @@ OSD_File::OSD_File() :
   myFileHandle (INVALID_HANDLE_VALUE),
 #else
   myFileChannel (-1),
-#endif
   myFILE (NULL),
+#endif
   myIO (0),
   myLock (OSD_NoLock),
   myMode (OSD_ReadWrite),
@@ -392,8 +392,8 @@ OSD_File::OSD_File (const OSD_Path& theName)
   myFileHandle (INVALID_HANDLE_VALUE),
 #else
   myFileChannel (-1),
-#endif
   myFILE (NULL),
+#endif
   myIO (0),
   myLock (OSD_NoLock),
   myMode (OSD_ReadWrite),
@@ -1617,39 +1617,6 @@ Standard_Boolean OSD_File::IsExecutable()
 }
 
 // =======================================================================
-// function : Capture
-// purpose  :
-// =======================================================================
-int OSD_File::Capture (int theDescr)
-{
-#ifdef _WIN32
-  // Get POSIX file descriptor from this file handle
-  int dFile = _open_osfhandle (reinterpret_cast<intptr_t>(myFileHandle), myMode);
-  if (0 > dFile)
-  {
-    _osd_wnt_set_error (myError, OSD_WFile, myFileHandle);
-    return -1;
-  }
-
-  // Duplicate an old file descriptor of the given one to be able to restore output to it later.
-  int oldDescr = _dup (theDescr);
-  // Redirect the output to this file
-  _dup2 (dFile, theDescr);
-
-  // Return the old descriptor
-  return oldDescr;
-#else
-  // Duplicate an old file descriptor of the given one to be able to restore output to it later.
-  int oldDescr = dup (theDescr);
-  // Redirect the output to this file
-  dup2 (myFileChannel, theDescr);
-
-  // Return the old descriptor
-  return oldDescr;
-#endif
-}
-
-// =======================================================================
 // function : Rewind
 // purpose  :
 // =======================================================================
index a1c0b3b..10f6a71 100644 (file)
@@ -182,31 +182,14 @@ public:
   //! Set file pointer position to the beginning of the file
   Standard_EXPORT void Rewind();
 
-  //! Redirect a standard handle (fileno(stdout), fileno(stdin) or 
-  //! fileno(stderr) to this OSD_File and return the copy of the original
-  //! standard handle.
-  //! Example:
-  //!    OSD_File aTmp;
-  //!    aTmp.BuildTemporary();
-  //!    int stdfd = _fileno(stdout);
-  //!
-  //!    int oldout = aTmp.Capture(stdfd);
-  //!    cout << "Some output to the file" << endl;
-  //!    cout << flush;
-  //!    fflush(stdout);
-  //!
-  //!    _dup2(oldout, stdfd); // Restore standard output
-  //!    aTmp.Close();
-  Standard_EXPORT int Capture(int theDescr);
-
 protected:
 
 #ifdef _WIN32
   Standard_Address myFileHandle;
 #else
   Standard_Integer myFileChannel;
-#endif
   Standard_Address myFILE;
+#endif
   Standard_Integer myIO;
 
 private:
index 7a90f39..f2ab1ee 100644 (file)
@@ -1264,43 +1264,6 @@ static Standard_Integer OCC24005 (Draw_Interpretor& theDI, Standard_Integer theN
   return 0;
 }
 
-#include <BRepAlgo_NormalProjection.hxx>
-static Standard_Integer OCC24012 (Draw_Interpretor& di, Standard_Integer argc, const char ** argv) 
-{
-       if (argc != 3) {
-               di << "Usage : " << argv[0] << " should be 2 arguments (face and edge)";
-               return 1;
-       }
-       
-       Handle(AIS_InteractiveContext) myAISContext = ViewerTest::GetAISContext();
-       if(myAISContext.IsNull()) {
-               di << "use 'vinit' command before " << argv[0] << "\n";
-               return 1;
-       }
-
-       TopoDS_Face m_Face1 = TopoDS::Face(DBRep::Get(argv[1]));
-       TopoDS_Edge m_Edge = TopoDS::Edge(DBRep::Get(argv[2]));
-       
-       BRepAlgo_NormalProjection anormpro(m_Face1);
-    anormpro.Add(m_Edge);
-    anormpro.SetDefaultParams();
-
-    //anormpro.Compute3d();
-    //anormpro.SetLimit();
-
-    anormpro.Build();
-
-    if (anormpro.IsDone())
-    {
-        TopoDS_Shape rshape = anormpro.Projection();
-               Handle(AIS_InteractiveObject) myShape = new AIS_Shape (rshape);
-               myAISContext->SetColor (myShape, Quantity_Color(Quantity_NOC_YELLOW), Standard_False);
-               myAISContext->Display (myShape, Standard_True);
-    }
-
-       return 0;
-}
-
 #include <BRepFeat_SplitShape.hxx>
 #include <ShapeAnalysis_ShapeContents.hxx>
 #include <BRepAlgo.hxx>
@@ -5305,7 +5268,6 @@ void QABugs::Commands_19(Draw_Interpretor& theCommands) {
   theCommands.Add ("OCC23972", "OCC23972", __FILE__, OCC23972, group);
   theCommands.Add ("OCC24370", "OCC24370 edge pcurve surface prec", __FILE__, OCC24370, group);
   theCommands.Add ("OCC24533", "OCC24533", __FILE__, OCC24533, group);
-  theCommands.Add ("OCC24012", "OCC24012 face edge", __FILE__, OCC24012, group);
   theCommands.Add ("OCC24086", "OCC24086 face wire", __FILE__, OCC24086, group);
   theCommands.Add ("OCC24667", "OCC24667 result Wire_spine Profile [Mode [Approx]], no args to get help", __FILE__, OCC24667, group);
   theCommands.Add ("OCC24755", "OCC24755", __FILE__, OCC24755, group);
index 31a660f..a27a3cb 100644 (file)
@@ -12,14 +12,18 @@ pload QAcommands
 igesread [locate_data_file bug24012_face.igs] face *
 igesread [locate_data_file bug24012_line.igs] edge *
 
+nproject r edge face
+checknbshapes r -edge 1
+checkprops r -l 5.96
+
 vinit
-OCC24012 face edge
 vsetdispmode 0
 vdisplay face
 vdisplay edge
+vdisplay r
+vsetcolor r yellow
 vfit
 
-
 set scale    71.101493567712652
 set proj_X   -0.14605970947882216
 set proj_Y   -0.18639384905183365
diff --git a/tests/demo/draw/dlog b/tests/demo/draw/dlog
new file mode 100644 (file)
index 0000000..0743b52
--- /dev/null
@@ -0,0 +1,39 @@
+# Check functionality of dlog command (capturing of stdout)
+
+pload MODELING
+
+# activate logging
+set status [dlog status]
+dlog on
+
+# check that log does not fail at many executions
+# note that this makes sense only if we do not run by test command with 
+# dlog already active, in which case puts is redefined
+if { $status == off } { 
+  for {set i 0} {$i < 10000} {incr i} { box b 1 1 1 }
+  dlog off
+  puts "Try puts to check that channel is not broken"
+  dlog on
+}
+
+# check that logging works as expected
+dlog reset
+box b 1 1 1
+explode b f
+if { $status == off } { 
+  # this will get to log if executed using "test" command instead of testgrid, as it redefines puts
+  puts "output of puts -- should not be in the log"
+}
+dlog add "output of dlog add"
+set log [dlog get]
+set expected "box b 1 1 1 \nexplode b f \nb_1 b_2 b_3 b_4 b_5 b_6\noutput of dlog add\n"
+if { $log != $expected } {
+  puts "Error: Logged text does not match expected"
+  puts "Value: \"$log\""
+  puts "Expected: \"$expected\""
+}
+
+# return original state of logging
+dlog $status
+
+puts "TEST COMPLETED"
index acfe3bb..f0604d2 100755 (executable)
@@ -3,7 +3,7 @@
 # check that path returned for command pload is as expected
 set expected src/Draw/Draw_PloadCommands.cxx
 set path [lindex [getsourcefile pload] 1]
-if { [string compare $path $expected] } {
+if { [string compare -nocase $path $expected] } {
     puts "Error: command 'getsourcefile pload' returned '$path' while expected '$expected'"
 }