user3540: Suppose I have a project with several modules and a main program.  This works well by adding the modules to main_program.
module1.cpp
module1.h
module2.cpp
module2.h
...
main_program.ino

I'm having problems when I have test programs for each of the modules.  These allow the various devices to be tested in isolation.
module1.cpp
module1.h
module1.ino # test program
module2.cpp
module2.h
module2.ino # test program
...
main_program.ino

I'm required to have each ino file in a directory of the same name.  I can add the files to main_program by adding (e.g.) ../module1/module1.{cpp,h}.  But, I discover that it has actually copied module1.{cpp,h} to main_program/.
How do I set this up?  Is there any option other than making each module a library?  If possible I would like to keep these files more local than the library directory, since they're specific to this project and not general-purpose libraries.

user64075: You have two options with ArduinoIDE. 
First Each module in a module directory 
So the structure looks like
  main_routine
     main_routine.ino
  --module1
      module1.ino  #test routine  
      module1.cpp
      module1.h   
 --module2
  .....

included in main_routine.ino
   #include "module1/module1.h"
   #include "module2/module2.h"

So you have everything in your master dir and in module specific subdirs below - Nothing is or will be copied by the ArduinoIDE (v1.8.12)
The second (more hacky way) is to have everything in one dir and for development testing working with include guards in the main_routine.ino
 // for production comment all MODULEX_TEST and uncomment PRODUCTION
 //#define PRODUCTION
 #define MODULE1_TEST 
 //#define MODULE2_TEST
.....
#if defined (PRODUCTION) || defined (MODULE1_TEST )
   #include "module1/module1.h"
#endif

#if defined (PRODUCTION) || defined (MODULE1_TEST )
   uint8_t myModule1Var = 0;
#endif

setup(){
.....
#if defined (PRODUCTION) || defined (MODULE1_TEST )
   myModule1setup someInit(1,2,3);
#endif
.....
 }

loop(){
.....
#if defined (PRODUCTION) || defined (MODULE1_TEST )
   void mymodule1Func(){....};
#endif 
#ifdef MODULE1_TEST
   void mymodule1Test(){....};
#endif 

Pros and cons

+This approach gives you the possibility to test two modules together. 
-Its harder to read, but 
+easier to document with tools like doxygen
~Only the active includes are compiled and so no overhead in the compiled code

Be aware that with some tool chains there are at the moment problems when using special templates (works if everything is in one module file - I use an "extended header" file as momentary work around) or arrays as initializer (open issue in esp8266)

3540: <p>Suppose I have a project with several modules and a main program.  This works well by adding the modules to main_program.</p>

<pre><code>module1.cpp
module1.h
module2.cpp
module2.h
...
main_program.ino
</code></pre>

<p>I'm having problems when I have test programs for each of the modules.  These allow the various devices to be tested in isolation.</p>

<pre><code>module1.cpp
module1.h
module1.ino # test program
module2.cpp
module2.h
module2.ino # test program
...
main_program.ino
</code></pre>

<p>I'm required to have each ino file in a directory of the same name.  I can add the files to main_program by adding (e.g.) ../module1/module1.{cpp,h}.  But, I discover that it has actually copied module1.{cpp,h} to main_program/.</p>

<p>How do I set this up?  Is there any option other than making each module a library?  If possible I would like to keep these files more local than the library directory, since they're specific to this project and not general-purpose libraries.</p>
 64075: <p>You have two options with ArduinoIDE. </p>

<p><strong>First</strong> Each module in a module directory </p>

<p>So the structure looks like</p>

<pre><code>  main_routine
     main_routine.ino
  --module1
      module1.ino  #test routine  
      module1.cpp
      module1.h   
 --module2
  .....
</code></pre>

<p>included in <code>main_routine.ino</code></p>

<pre><code>   #include "module1/module1.h"
   #include "module2/module2.h"
</code></pre>

<p>So you have everything in your master dir and in module specific subdirs below - Nothing is or will be copied by the ArduinoIDE (v1.8.12)</p>

<p>The <strong>second</strong> (more hacky way) is to have everything in one dir and for development testing working with include guards in the <code>main_routine.ino</code></p>

<pre><code> // for production comment all MODULEX_TEST and uncomment PRODUCTION
 //#define PRODUCTION
 #define MODULE1_TEST 
 //#define MODULE2_TEST
.....
#if defined (PRODUCTION) || defined (MODULE1_TEST )
   #include "module1/module1.h"
#endif

#if defined (PRODUCTION) || defined (MODULE1_TEST )
   uint8_t myModule1Var = 0;
#endif

setup(){
.....
#if defined (PRODUCTION) || defined (MODULE1_TEST )
   myModule1setup someInit(1,2,3);
#endif
.....
 }

loop(){
.....
#if defined (PRODUCTION) || defined (MODULE1_TEST )
   void mymodule1Func(){....};
#endif 
#ifdef MODULE1_TEST
   void mymodule1Test(){....};
#endif 
</code></pre>

<p>Pros and cons</p>

<ul>
<li>+This approach gives you the possibility to test two modules together. </li>
<li>-Its harder to read, but </li>
<li>+easier to document with tools like doxygen</li>
<li>~Only the active includes are compiled and so no overhead in the compiled code</li>
</ul>

<p>Be aware that with some tool chains there are at the moment problems when using special templates (works if everything is in one module file - I use an "extended header" file as momentary work around) or arrays as initializer (open issue in esp8266)</p>
