From ca510f6c299335c8db27c65c10a8553801c06023 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Mon, 5 Jun 2023 22:08:26 -0400
Subject: [PATCH] SL-18837: Try giving temp Python scripts a .py extension.

On GitHub Windows Actions runners, we're getting permissions errors trying to
tell the Python interpreter to run a NamedTempFile script. Try using
NamedExtTempFile to give each such script a .py extension.
---
 indra/llcommon/tests/llleap_test.cpp    | 222 ++++++++++++------------
 indra/llcommon/tests/llprocess_test.cpp |   4 +-
 2 files changed, 113 insertions(+), 113 deletions(-)

diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index 60005fc6a95..99fd073dd2c 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -213,9 +213,9 @@ namespace tut
     void object::test<1>()
     {
         set_test_name("multiple LLLeap instances");
-        NamedTempFile script("py",
-                             "import time\n"
-                             "time.sleep(1)\n");
+        NamedExtTempFile script("py",
+                                "import time\n"
+                                "time.sleep(1)\n");
         LLLeapVector instances;
         instances.push_back(LLLeap::create(get_test_name(),
                                            sv(list_of(PYTHON)(script.getName())))->getWeak());
@@ -231,10 +231,10 @@ namespace tut
     void object::test<2>()
     {
         set_test_name("stderr to log");
-        NamedTempFile script("py",
-                             "import sys\n"
-                             "sys.stderr.write('''Hello from Python!\n"
-                             "note partial line''')\n");
+        NamedExtTempFile script("py",
+                                "import sys\n"
+                                "sys.stderr.write('''Hello from Python!\n"
+                                "note partial line''')\n");
         StringVec vcommand{ PYTHON, script.getName() };
         CaptureLog log(LLError::LEVEL_INFO);
         waitfor(LLLeap::create(get_test_name(), vcommand));
@@ -246,8 +246,8 @@ namespace tut
     void object::test<3>()
     {
         set_test_name("bad stdout protocol");
-        NamedTempFile script("py",
-                             "print('Hello from Python!')\n");
+        NamedExtTempFile script("py",
+                                "print('Hello from Python!')\n");
         CaptureLog log(LLError::LEVEL_WARN);
         waitfor(LLLeap::create(get_test_name(),
                                sv(list_of(PYTHON)(script.getName()))));
@@ -259,10 +259,10 @@ namespace tut
     void object::test<4>()
     {
         set_test_name("leftover stdout");
-        NamedTempFile script("py",
-                             "import sys\n"
-                             // note lack of newline
-                             "sys.stdout.write('Hello from Python!')\n");
+        NamedExtTempFile script("py",
+                                "import sys\n"
+                                // note lack of newline
+                                "sys.stdout.write('Hello from Python!')\n");
         CaptureLog log(LLError::LEVEL_WARN);
         waitfor(LLLeap::create(get_test_name(),
                                sv(list_of(PYTHON)(script.getName()))));
@@ -274,9 +274,9 @@ namespace tut
     void object::test<5>()
     {
         set_test_name("bad stdout len prefix");
-        NamedTempFile script("py",
-                             "import sys\n"
-                             "sys.stdout.write('5a2:something')\n");
+        NamedExtTempFile script("py",
+                                "import sys\n"
+                                "sys.stdout.write('5a2:something')\n");
         CaptureLog log(LLError::LEVEL_WARN);
         waitfor(LLLeap::create(get_test_name(),
                                sv(list_of(PYTHON)(script.getName()))));
@@ -381,16 +381,16 @@ namespace tut
         set_test_name("round trip");
         AckAPI api;
         Result result;
-        NamedTempFile script("py",
-                             boost::phoenix::placeholders::arg1 <<
-                             "from " << reader_module << " import *\n"
-                             // make a request on our little API
-                             "request(pump='" << api.getName() << "', data={})\n"
-                             // wait for its response
-                             "resp = get()\n"
-                             "result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
-                             "            else 'bad: ' + str(resp)\n"
-                             "send(pump='" << result.getName() << "', data=result)\n");
+        NamedExtTempFile script("py",
+                                boost::phoenix::placeholders::arg1 <<
+                                "from " << reader_module << " import *\n"
+                                // make a request on our little API
+                                "request(pump='" << api.getName() << "', data={})\n"
+                                // wait for its response
+                                "resp = get()\n"
+                                "result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
+                                "            else 'bad: ' + str(resp)\n"
+                                "send(pump='" << result.getName() << "', data=result)\n");
         waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))));
         result.ensure();
     }
@@ -419,37 +419,37 @@ namespace tut
         // iterations etc. in OS pipes and the LLLeap/LLProcess implementation.
         ReqIDAPI api;
         Result result;
-        NamedTempFile script("py",
-                             boost::phoenix::placeholders::arg1 <<
-                             "import sys\n"
-                             "from " << reader_module << " import *\n"
-                             // Note that since reader imports llsd, this
-                             // 'import *' gets us llsd too.
-                             "sample = llsd.format_notation(dict(pump='" <<
-                             api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
-                             // The whole packet has length prefix too: "len:data"
-                             "samplen = len(str(len(sample))) + 1 + len(sample)\n"
-                             // guess how many messages it will take to
-                             // accumulate BUFFERED_LENGTH
-                             "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
-                             "print('Sending %s requests' % count, file=sys.stderr)\n"
-                             "for i in range(count):\n"
-                             "    request('" << api.getName() << "', dict(reqid=i))\n"
-                             // The assumption in this specific test that
-                             // replies will arrive in the same order as
-                             // requests is ONLY valid because the API we're
-                             // invoking sends replies instantly. If the API
-                             // had to wait for some external event before
-                             // sending its reply, replies could arrive in
-                             // arbitrary order, and we'd have to tick them
-                             // off from a set.
-                             "result = ''\n"
-                             "for i in range(count):\n"
-                             "    resp = get()\n"
-                             "    if resp['data']['reqid'] != i:\n"
-                             "        result = 'expected reqid=%s in %s' % (i, resp)\n"
-                             "        break\n"
-                             "send(pump='" << result.getName() << "', data=result)\n");
+        NamedExtTempFile script("py",
+                                boost::phoenix::placeholders::arg1 <<
+                                "import sys\n"
+                                "from " << reader_module << " import *\n"
+                                // Note that since reader imports llsd, this
+                                // 'import *' gets us llsd too.
+                                "sample = llsd.format_notation(dict(pump='" <<
+                                api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
+                                // The whole packet has length prefix too: "len:data"
+                                "samplen = len(str(len(sample))) + 1 + len(sample)\n"
+                                // guess how many messages it will take to
+                                // accumulate BUFFERED_LENGTH
+                                "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
+                                "print('Sending %s requests' % count, file=sys.stderr)\n"
+                                "for i in range(count):\n"
+                                "    request('" << api.getName() << "', dict(reqid=i))\n"
+                                // The assumption in this specific test that
+                                // replies will arrive in the same order as
+                                // requests is ONLY valid because the API we're
+                                // invoking sends replies instantly. If the API
+                                // had to wait for some external event before
+                                // sending its reply, replies could arrive in
+                                // arbitrary order, and we'd have to tick them
+                                // off from a set.
+                                "result = ''\n"
+                                "for i in range(count):\n"
+                                "    resp = get()\n"
+                                "    if resp['data']['reqid'] != i:\n"
+                                "        result = 'expected reqid=%s in %s' % (i, resp)\n"
+                                "        break\n"
+                                "send(pump='" << result.getName() << "', data=result)\n");
         waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))),
                 300);               // needs more realtime than most tests
         result.ensure();
@@ -462,60 +462,60 @@ namespace tut
     {
         ReqIDAPI api;
         Result result;
-        NamedTempFile script("py",
-                             boost::phoenix::placeholders::arg1 <<
-                             "import sys\n"
-                             "from " << reader_module << " import *\n"
-                             // Generate a very large string value.
-                             "desired = int(sys.argv[1])\n"
-                             // 7 chars per item: 6 digits, 1 comma
-                             "count = int((desired - 50)/7)\n"
-                             "large = ''.join('%06d,' % i for i in range(count))\n"
-                             // Pass 'large' as reqid because we know the API
-                             // will echo reqid, and we want to receive it back.
-                             "request('" << api.getName() << "', dict(reqid=large))\n"
-                             "try:\n"
-                             "    resp = get()\n"
-                             "except ParseError as e:\n"
-                             "    # try to find where e.data diverges from expectation\n"
-                             // Normally we'd expect a 'pump' key in there,
-                             // too, with value replypump(). But Python
-                             // serializes keys in a different order than C++,
-                             // so incoming data start with 'data'.
-                             // Truthfully, though, if we get as far as 'pump'
-                             // before we find a difference, something's very
-                             // strange.
-                             "    expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
-                             "    chunk = 40\n"
-                             "    for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
-                             "        if e.data[offset:offset+chunk] != \\\n"
-                             "           expect[offset:offset+chunk]:\n"
-                             "            print('Offset %06d: expect %r,\\n'\\\n"
-                             "                                '                  get %r' %\\\n"
-                             "                                (offset,\n"
-                             "                                 expect[offset:offset+chunk],\n"
-                             "                                 e.data[offset:offset+chunk]),\n"
-                             "                                 file=sys.stderr)\n"
-                             "            break\n"
-                             "    else:\n"
-                             "        print('incoming data matches expect?!', file=sys.stderr)\n"
-                             "    send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
-                             "    sys.exit(1)\n"
-                             "\n"
-                             "echoed = resp['data']['reqid']\n"
-                             "if echoed == large:\n"
-                             "    send('" << result.getName() << "', '')\n"
-                             "    sys.exit(0)\n"
-                             // Here we know echoed did NOT match; try to find where
-                             "for i in range(count):\n"
-                             "    start = 7*i\n"
-                             "    end   = 7*(i+1)\n"
-                             "    if end > len(echoed)\\\n"
-                             "    or echoed[start:end] != large[start:end]:\n"
-                             "        send('" << result.getName() << "',\n"
-                             "             'at offset %s, expected %r but got %r' %\n"
-                             "             (start, large[start:end], echoed[start:end]))\n"
-                             "sys.exit(1)\n");
+        NamedExtTempFile script("py",
+                                boost::phoenix::placeholders::arg1 <<
+                                "import sys\n"
+                                "from " << reader_module << " import *\n"
+                                // Generate a very large string value.
+                                "desired = int(sys.argv[1])\n"
+                                // 7 chars per item: 6 digits, 1 comma
+                                "count = int((desired - 50)/7)\n"
+                                "large = ''.join('%06d,' % i for i in range(count))\n"
+                                // Pass 'large' as reqid because we know the API
+                                // will echo reqid, and we want to receive it back.
+                                "request('" << api.getName() << "', dict(reqid=large))\n"
+                                "try:\n"
+                                "    resp = get()\n"
+                                "except ParseError as e:\n"
+                                "    # try to find where e.data diverges from expectation\n"
+                                // Normally we'd expect a 'pump' key in there,
+                                // too, with value replypump(). But Python
+                                // serializes keys in a different order than C++,
+                                // so incoming data start with 'data'.
+                                // Truthfully, though, if we get as far as 'pump'
+                                // before we find a difference, something's very
+                                // strange.
+                                "    expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
+                                "    chunk = 40\n"
+                                "    for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
+                                "        if e.data[offset:offset+chunk] != \\\n"
+                                "           expect[offset:offset+chunk]:\n"
+                                "            print('Offset %06d: expect %r,\\n'\\\n"
+                                "                                '                  get %r' %\\\n"
+                                "                                (offset,\n"
+                                "                                 expect[offset:offset+chunk],\n"
+                                "                                 e.data[offset:offset+chunk]),\n"
+                                "                                 file=sys.stderr)\n"
+                                "            break\n"
+                                "    else:\n"
+                                "        print('incoming data matches expect?!', file=sys.stderr)\n"
+                                "    send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
+                                "    sys.exit(1)\n"
+                                "\n"
+                                "echoed = resp['data']['reqid']\n"
+                                "if echoed == large:\n"
+                                "    send('" << result.getName() << "', '')\n"
+                                "    sys.exit(0)\n"
+                                // Here we know echoed did NOT match; try to find where
+                                "for i in range(count):\n"
+                                "    start = 7*i\n"
+                                "    end   = 7*(i+1)\n"
+                                "    if end > len(echoed)\\\n"
+                                "    or echoed[start:end] != large[start:end]:\n"
+                                "        send('" << result.getName() << "',\n"
+                                "             'at offset %s, expected %r but got %r' %\n"
+                                "             (start, large[start:end], echoed[start:end]))\n"
+                                "sys.exit(1)\n");
         waitfor(LLLeap::create(test_name,
                                sv(list_of
                                   (PYTHON)
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index 81449b4a421..4adb8d872a4 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -191,7 +191,7 @@ struct PythonProcessLauncher
     LLProcess::Params mParams;
     LLProcessPtr mPy;
     std::string mDesc;
-    NamedTempFile mScript;
+    NamedExtTempFile mScript;
 };
 
 /// convenience function for PythonProcessLauncher::run()
@@ -355,7 +355,7 @@ namespace tut
         set_test_name("raw APR nonblocking I/O");
 
         // Create a script file in a temporary place.
-        NamedTempFile script("py",
+        NamedExtTempFile script("py",
             "from __future__ import print_function" EOL
             "import sys" EOL
             "import time" EOL
-- 
GitLab