<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://damienfremont.com/feed.xml" rel="self" type="application/atom+xml" /><link href="http://damienfremont.com/" rel="alternate" type="text/html" /><updated>2026-10-10T23:33:13+00:00</updated><id>http://damienfremont.com/feed.xml</id><title type="html">Damien Fremont</title><subtitle>Hi, I&apos;m Damien Fremont. Software Developer, 13 years XP (Vancouver, Canada).</subtitle><author><name>Damien Fremont</name></author><entry><title type="html">3D Game Starter – C99 OpenGL3 Raylib5 CMake (2500 FPS!)</title><link href="http://damienfremont.com/2026/01/11/3d-game-starter-c99-raylib5-opengl3" rel="alternate" type="text/html" title="3D Game Starter – C99 OpenGL3 Raylib5 CMake (2500 FPS!)" /><published>2026-01-11T00:00:00+00:00</published><updated>2026-01-11T00:00:00+00:00</updated><id>http://damienfremont.com/2026/01/11/3d-game-starter-c99-raylib5-opengl3</id><content type="html" xml:base="http://damienfremont.com/2026/01/11/3d-game-starter-c99-raylib5-opengl3"><![CDATA[<p><img src="/upload/20260111-screenshot-raylib.gif" alt="post-thumbnail" /></p>

<p>2024’s small template to start your raylib game. Featuring 3D, C 2017, Raylib 5.0, OpenGL 3 and a modular architecture.</p>

<h2 id="content">Content</h2>

<ul>
  <li><a href="#dependencies">Dependencies</a></li>
  <li><a href="#screenshots">Screenshots</a></li>
  <li><a href="#features">Features</a></li>
  <li><a href="#install">Install</a></li>
  <li><a href="#usage">Usage</a></li>
  <li><a href="#assets">Assets</a></li>
  <li><a href="#release">Release</a></li>
  <li><a href="#resources">Resources</a></li>
</ul>

<hr />

<h2 id="dependencies">Dependencies</h2>

<ul>
  <li>C++ 2020 / C 2017</li>
  <li><a href="https://www.raylib.com/">RayLib 5.0</a></li>
  <li>OpenGL 3.30</li>
  <li>CMake v3.28.3</li>
  <li>Visual Studio 2022 MVS</li>
</ul>

<h2 id="screenshots">Screenshots</h2>

<p>Note: Captured on mid-tier PC from 2020 (GPU: RTX-2060 with 6 GB VRAM, CPU: Intel i5-9600K, RAM: 32 GB).</p>

<p><img src="/upload/20260111-screenshot-raylib.png" alt="alt text" /></p>

<p><img src="/upload/20260111-screenshot-unreal.jpg" alt="alt text" /></p>

<p><img src="/upload/20260111-screenshot-raylib-2.png" alt="alt text" /></p>

<p><img src="/upload/20260111-diagrams-1.png" alt="alt text" /></p>

<p><img src="/upload/20260111-diagrams-2.png" alt="alt text" /></p>

<h2 id="features">Features</h2>

<ul>
  <li>Targets:
    <ul class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Win64 (Windows)</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Linux</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />SteamDeck (Windows/Linux)</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />HTML5 (WebAssembly)</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Discord (WebAssembly)</li>
    </ul>
  </li>
  <li>Graphics
    <ul class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Render: PostProcessing Bloom, AA MSAA, Texture Filtering</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Camera: ThirdPerson, Follow</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Lighting: World Lighting, Player Lighting, Player Shadows</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Meshes: Meshes, Textures, Shaders, Materials, 
  [ ] Texture tiling</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Sky: Color, Cubemap, Fog</li>
    </ul>
  </li>
  <li>World
    <ul class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Physics
  [ ] hitbox
  [ ] gravity</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Simulation: Tick Systems</li>
    </ul>
  </li>
  <li>Scripting
    <ul class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Console</li>
    </ul>
  </li>
  <li>Animation
    <ul class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Character</li>
    </ul>
  </li>
  <li>Input
    <ul class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />UI: lauch screen, settings menu</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Input: Keyboard, Arrows, Gamepad</li>
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Control: [x] Tank control, [ ] Third Person</li>
    </ul>
  </li>
  <li>Other
    <ul class="task-list">
      <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />launching screen</li>
    </ul>
  </li>
</ul>

<p>Repository layout:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">├──</span><span class="w"> </span><span class="n">assets</span><span class="w">
</span><span class="err">│  </span><span class="w"> </span><span class="err">└──</span><span class="w"> </span><span class="nx">images</span><span class="w">
</span><span class="err">│  </span><span class="w"> </span><span class="err">└──</span><span class="w"> </span><span class="o">...</span><span class="w">
</span><span class="err">├──</span><span class="w"> </span><span class="n">docs</span><span class="w">
</span><span class="err">├──</span><span class="w"> </span><span class="nx">infra</span><span class="w">
</span><span class="err">├──</span><span class="w"> </span><span class="n">lib</span><span class="w">
</span><span class="err">│  </span><span class="w"> </span><span class="err">├──</span><span class="w"> </span><span class="nx">raylib-5.0_win64_msvc16</span><span class="w">
</span><span class="err">│  </span><span class="w"> </span><span class="err">├──</span><span class="w"> </span><span class="n">winmm-10-win64</span><span class="w">
</span><span class="err">├──</span><span class="w"> </span><span class="nx">platform</span><span class="w">
</span><span class="err">│  </span><span class="w"> </span><span class="err">├──</span><span class="w"> </span><span class="n">engine</span><span class="w">
</span><span class="err">│  </span><span class="w"> </span><span class="err">└──</span><span class="w"> </span><span class="nx">game</span><span class="w">
</span><span class="err">├──</span><span class="w"> </span><span class="n">standalone</span><span class="w">
</span><span class="err">│</span><span class="w">   </span><span class="err">├──</span><span class="w"> </span><span class="nx">desktop_win64</span><span class="w">
</span><span class="err">└──</span><span class="w"> </span><span class="n">tools</span><span class="w">
 </span><span class="err">  </span><span class="w"> </span><span class="err">└──</span><span class="w"> </span><span class="nx">blender</span><span class="w">
 </span><span class="err">  </span><span class="w"> </span><span class="err">└──</span><span class="w"> </span><span class="n">ci</span><span class="w">
</span></code></pre></div></div>

<h2 id="install">Install</h2>

<h3 id="windows">Windows</h3>

<p>CMake:</p>
<ul>
  <li>Download the <code class="language-plaintext highlighter-rouge">v3.28.3</code> release of <code class="language-plaintext highlighter-rouge">CMake</code> at http://www.cmake.org/download/.</li>
  <li>Pick Windows (<code class="language-plaintext highlighter-rouge">Windows x64 Installer</code>).</li>
  <li>Run the installer</li>
  <li>When asked for, select <code class="language-plaintext highlighter-rouge">Add CMake to the system PATH for all users</code>.</li>
  <li>Run software installation…</li>
</ul>

<p>Visual Studio:</p>
<ul>
  <li>Download the <code class="language-plaintext highlighter-rouge">Community</code> version of <code class="language-plaintext highlighter-rouge">Visual Studio 2022</code> release of Visual Studio at https://visualstudio.microsoft.com/downloads/</li>
  <li>Run the installer…</li>
  <li>Run software <code class="language-plaintext highlighter-rouge">Visual Studio Installer</code></li>
  <li>When asked for, click on <code class="language-plaintext highlighter-rouge">modify</code> under <code class="language-plaintext highlighter-rouge">Visual Studio 2002</code></li>
  <li>Select <code class="language-plaintext highlighter-rouge">Desktop development with C++</code> (contains MVC package)</li>
  <li>Click on <code class="language-plaintext highlighter-rouge">modify</code></li>
</ul>

<p>PowerShell:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cmake</span><span class="w"> </span><span class="nt">-S</span><span class="w"> </span><span class="s2">"."</span><span class="w"> </span><span class="nt">-B</span><span class="w"> </span><span class="s2">"build"</span><span class="w"> </span><span class="nt">-G</span><span class="w"> </span><span class="s2">"Visual Studio 17 2022"</span><span class="w">
</span></code></pre></div></div>

<h2 id="usage">Usage</h2>

<h3 id="cli-pure-cmake">CLI (pure CMake)</h3>

<ul>
  <li>Pros: basic, no Editor, CI friendly.</li>
  <li>Cons: manual actions intensive.</li>
</ul>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="n">\tools\ci\install.bat</span><span class="w">

</span><span class="o">.</span><span class="n">\tools\ci\dev.bat</span><span class="w">
</span><span class="c"># OR</span><span class="w">
</span><span class="o">.</span><span class="n">\tools\ci\release.bat</span><span class="w">
</span></code></pre></div></div>

<h3 id="visualstudio-2022-ide">VisualStudio 2022 (IDE)</h3>

<ul>
  <li>Pros: better integration, 1 step compile+debug.</li>
  <li>Cons: very laggy UI, bad search tools.</li>
</ul>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="n">\tools\ci\install.bat</span><span class="w">
</span></code></pre></div></div>

<p><img src="/upload/20260111-thumb-vs-open.png" alt="alt text" /></p>

<p><img src="/upload/20260111-thumb-vs-debug.png" alt="alt text" /></p>

<h3 id="vscode">VSCode</h3>

<ul>
  <li>Pros: better code editor, lightweight.</li>
  <li>Cons: 2 steps compile+debug</li>
</ul>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="n">\tools\ci\install.bat</span><span class="w">

</span><span class="o">.</span><span class="n">\tools\ci\watch.bat</span><span class="w">
</span></code></pre></div></div>

<p>Run (press Ctrl+F5, or Menu &gt; “Run” &gt; “Run without Debugging”)</p>

<p>Debug (press F5, or Menu &gt; “Run” &gt; “Start Debugging”)</p>

<p><img src="/upload/20260111-thumb-vsc-debug.png" alt="alt text" /></p>

<h2 id="assets">Assets</h2>

<h3 id="pipeline">Pipeline</h3>

<p>Installation:</p>

<ol>
  <li>Download <a href="./tools/blender/io_scene_m3d.py">io_scene_m3d.py</a></li>
  <li>Start Blender</li>
  <li>Go to menu, “Edit” &gt; “Preferences…” &gt; “Add-ons” tab &gt; “Install…” button</li>
  <li>Browse and double click on the downloaded file</li>
  <li>On the “Add-ons” tab, search for “m3d”</li>
  <li>Click on “Enable Add-on.”</li>
  <li>Now in the menu, “File” &gt; “Export” &gt; “Model 3D (.m3d)” (and eventually “File” &gt; “Import” &gt; “Model 3D (.m3d)”) should appear</li>
</ol>

<p>Get 3D Models:</p>

<ol>
  <li>Start WebBrowser…</li>
  <li>https://www.mixamo.com</li>
  <li>Click Characters</li>
  <li>search “X bot”</li>
  <li>Click Download</li>
  <li>Check Format: fbx, pose: t-pose</li>
  <li>Click Download</li>
  <li>Start Blender…</li>
  <li>Go to menu, File &gt; New &gt; General …Delete Cube</li>
  <li>Go to menu, File &gt; Import &gt; FBX &gt; “X bot.fbx”</li>
  <li>Click on the “Texture Paint” tab &gt; click “new” &gt; type “X_bot_diffuse”</li>
  <li>On right side panel &gt; click “material” &gt; “base color” &gt; “image texture” &gt; select “X_bot_diffuse”</li>
  <li>Go to menu, File &gt; Export &gt; M3D &gt; scale: 2.0 &gt; “X_bot.m3d” &gt; Export</li>
</ol>

<p>Get 3D Animations:</p>

<ol>
  <li>Start WebBrowser…</li>
  <li>https://www.mixamo.com</li>
  <li>Click Animations</li>
  <li>search “Slow Run”</li>
  <li>check “In Place” box</li>
  <li>Click Download</li>
  <li>Check Format: fbx, Skin: with skin</li>
  <li>Click Download</li>
  <li>Start Blender…</li>
  <li>Go to menu, File &gt; New &gt; General …Delete Cube</li>
  <li>Go to menu, File &gt; Import &gt; FBX &gt; “Slow Run.fbx”</li>
  <li>Click on the “Animation” tab &gt; click “editor type icon” &gt; “non linear animation” (new channel editor)</li>
  <li>click “push down action” (new channel)</li>
  <li>Go to menu, File &gt; Export &gt; M3D &gt; scale: 2.0 &gt; “Slow Run.m3d” &gt; Export</li>
</ol>

<h2 id="faq">FAQ</h2>

<h3 id="why-models-are-sometime-unlit-">Why Models are sometime unlit ?</h3>

<p>Cause: Blender export changes materials indexes (start with 0 or 1) and lighting shader needs to be applied to real material.</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">*.obj</code>, <code class="language-plaintext highlighter-rouge">*.iqm</code> formats: use <code class="language-plaintext highlighter-rouge">material[0]</code></li>
  <li><code class="language-plaintext highlighter-rouge">*.m3d</code>, <code class="language-plaintext highlighter-rouge">*.glb</code> formats: use <code class="language-plaintext highlighter-rouge">material[1]</code></li>
</ul>

<p><img src="/upload/20260111-faq-character-lighting.png" alt="alt text" /></p>

<h3 id="what-about-performance-">What about performance ?</h3>

<p>Condition: GPU: RTX-2060, CPU: i5-9600K, RAM: 32 GB</p>
<ul>
  <li>Raylib (GL3) “High”: <code class="language-plaintext highlighter-rouge">~2500 FPS</code> (AA <code class="language-plaintext highlighter-rouge">4x</code>, Postpro <code class="language-plaintext highlighter-rouge">Bloom</code>)</li>
  <li>Raylib (GL3) “Low”: <code class="language-plaintext highlighter-rouge">~3000 FPS</code></li>
  <li>Unreal (DX12) “Low”: <code class="language-plaintext highlighter-rouge">~150 FPS</code> (Settings=<code class="language-plaintext highlighter-rouge">Low</code>)</li>
</ul>

<p><img src="/upload/20260111-faq-performance-fps.png" alt="alt text" /></p>

<p><img src="/upload/20260111-faq-performance-fps-2.png" alt="alt text" /></p>

<h2 id="project-history">Project History</h2>

<p><img src="/upload/20260111-history-screenshot.png" alt="alt text" /></p>

<h2 id="release">Release</h2>

<ul>
  <li>Source Code (monorepo):
    <ul>
      <li><a href="/upload/20260111-c99-raylib5-gl3-3dgame-starter-1.0.0-source-code">20260111-c99-raylib5-gl3-3dgame-starter-1.0.0-source-code</a></li>
    </ul>
  </li>
  <li>Executable (Win64):
    <ul>
      <li><a href="/upload/20260111-c99-raylib5-gl3-3dgame-starter-1.0.0-desktop_win64.zip">20260111-c99-raylib5-gl3-3dgame-starter-1.0.0-desktop_win64.zip</a></li>
    </ul>
  </li>
  <li>Repository (GitHub):
    <ul>
      <li><a href="https://github.com/DamienFremont/c99-raylib5-gl3-3dgame-starter">c99-raylib5-gl3-3dgame-starter (GitHub)</a></li>
    </ul>
  </li>
</ul>

<h2 id="resources">Resources</h2>

<ul>
  <li>C
    <ul>
      <li><a href="https://github.com/TheLartians/ModernCppStarter/tree/master">ModernCppStarter</a></li>
    </ul>
  </li>
  <li>CMake
    <ul>
      <li><a href="https://stackoverflow.com/questions/8304190/cmake-with-include-and-source-paths-basic-setup">CMake with include and source paths - basic setup</a></li>
      <li><a href="http://www.cunningplanning.com/post/cmake-add-library/">CMake add_library</a></li>
      <li><a href="https://cmake.org/cmake/help/latest/prop_dir/VS_STARTUP_PROJECT.html">Specify the default startup project in a Visual Studio solution.</a></li>
      <li><a href="https://medium.com/@damngoodtech/how-to-automatically-build-compile-and-test-cmake-c-unit-tests-on-file-save-9e7fe259e7ae">How To Automatically Build CMake</a></li>
    </ul>
  </li>
  <li>Monorepo
    <ul>
      <li><a href="https://lucapette.me/writing/how-to-structure-a-monorepo/?utm_source=atom_feed">How to structure a monorepo</a></li>
      <li><a href="https://levelup.gitconnected.com/a-monorepo-template-that-actually-works-3efd87770fa5">A monorepo template that actually works</a></li>
    </ul>
  </li>
  <li>VS Code
    <ul>
      <li><a href="https://code.visualstudio.com/docs/cpp/launch-json-reference">Configure C/C++ debugging</a></li>
    </ul>
  </li>
  <li>Raylib
    <ul>
      <li><a href="https://gist.github.com/raysan5/17392498d40e2cb281f5d09c0a4bf798">raylib vs SDL - A libraries comparison</a></li>
      <li><a href="https://github.com/raysan5/raylib-game-template">raylib-game-template</a></li>
      <li><a href="https://github.com/raysan5/raylib/blob/master/examples/">Examples GitHub</a></li>
      <li><a href="https://www.raylib.com/examples.html">Examples Demos (HTML5)</a>
        <ul>
          <li><a href="https://www.raylib.com/examples/shaders/loader.html?name=shaders_postprocessing">shaders_postprocessing</a></li>
          <li><a href="https://www.raylib.com/examples/shaders/loader.html?name=shaders_texture_tiling">shaders_texture_tiling</a></li>
          <li><a href="https://www.raylib.com/examples/shaders/loader.html?name=shaders_basic_lighting">shaders_basic_lighting</a></li>
          <li><a href="https://www.raylib.com/examples/shaders/loader.html?name=shaders_fog">shaders_fog</a></li>
          <li><a href="https://www.raylib.com/examples/text/loader.html?name=text_draw_3d">text_draw_3d</a></li>
          <li><a href="https://www.raylib.com/examples/textures/loader.html?name=textures_image_processing">textures_image_processing</a></li>
        </ul>
      </li>
      <li><a href="https://sepi.prose.sh/2022-09-11-how-to-properly-use-shaders-in-raylib">how to properly setup raylib app to talk to glsl shaders</a></li>
      <li>WASM
        <ul>
          <li><a href="https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5)">Working for Web (HTML5)</a></li>
          <li><a href="https://github.com/raysan5/raylib/wiki/Creating-Discord-Activities-with-raylib">Creating Discord Activities with raylib</a></li>
        </ul>
      </li>
      <li>Shadow
        <ul>
          <li><a href="https://github.com/WEREMSOFT/c99-raylib-shadowmap/tree/master">c99-raylib-shadowmap</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Quality
    <ul>
      <li><a href="https://cppcheck.sourceforge.io/">cppcheck</a></li>
    </ul>
  </li>
  <li>Model3D Format
    <ul>
      <li><a href="https://bztsrc.gitlab.io/model3d/">Welcome to Model 3D!</a></li>
      <li><a href="https://gitlab.com/bztsrc/model3d/tree/master/blender/">Model 3D Blender Integration</a></li>
      <li><a href="https://gitlab.com/bztsrc/model3d/-/blob/master/blender/io_scene_m3d.py">io_scene_m3d.py</a></li>
      <li><a href="https://www.youtube.com/watch?v=x6Fw62lX5Dg">IQM Export</a></li>
    </ul>
  </li>
  <li>C Libs
    <ul>
      <li><a href="https://github.com/DaveGamble/cJSON">cJSON parser</a></li>
    </ul>
  </li>
</ul>

<h2 id="references">References</h2>

<p>There are several games made with raylib to highlight:</p>
<ul>
  <li>Hare136: https://play.google.com/store/apps/details?id=games.haremonic.slider</li>
  <li>SideStep Legends: https://store.steampowered.com/app/2077590/Sidestep_Legends/</li>
  <li>Pure Logic: https://store.steampowered.com/app/1861500/Pure_Logic/</li>
</ul>

<p>There are also many small games (and tools) made with raylib on itchio: https://itch.io/c/380928/made-with-raylib</p>]]></content><author><name>Damien Fremont</name></author><category term="gamedev" /><category term="c" /><category term="opengl" /><category term="raylib" /><category term="cmake" /><summary type="html"><![CDATA[2024's small template to start your raylib game. Featuring 3D, C 2017, Raylib 5.0, OpenGL 3 and a modular architecture.]]></summary></entry><entry><title type="html">My Mobile Game Cars-2048 Is Open-Source Now!</title><link href="http://damienfremont.com/2022/03/01/cars2048-open-source-release" rel="alternate" type="text/html" title="My Mobile Game Cars-2048 Is Open-Source Now!" /><published>2022-03-01T00:00:00+00:00</published><updated>2022-03-01T00:00:00+00:00</updated><id>http://damienfremont.com/2022/03/01/cars2048-open-source-release</id><content type="html" xml:base="http://damienfremont.com/2022/03/01/cars2048-open-source-release"><![CDATA[<p><img src="/upload/opensource.png" alt="post-thumbnail" /></p>

<p>Hi everyone,</p>

<p>This is the complete source code for my game Cars2048 (Android, 2017, Unity3D)</p>

<p>Check it out here :</p>
<ul>
  <li>
    <p><a href="https://github.com/FremontGames/Cars2048">Unity project on GitHub</a></p>
  </li>
  <li>
    <p><a href="https://github.com/FremontGames/Cars2048-assets">Photoshop and Blender assets on GitHub</a></p>
  </li>
</ul>

<p>The primary intent of this release is for entertainment and educational purposes.</p>

<p>Have fun !</p>

<hr />

<h2 id="store">Store</h2>

<p>GooglePlay/Android : <a href="https://play.google.com/store/apps/details?id=com.atalantoo.game20483dcars">Cars 2048 - Puzzle Game</a></p>

<h2 id="screenshots">Screenshots</h2>

<p><img src="/upload/20170900_113215.gif" alt="screenshot" /></p>

<p><img src="/upload/app-image.png" alt="screenshot" /></p>

<p><img src="/upload/Screen-Shot-12-30-18-at-10.02-PM.png" alt="screenshot" /></p>]]></content><author><name>Damien Fremont</name></author><category term="gamedev" /><category term="unity3d" /><category term="git" /><summary type="html"><![CDATA[Hi everyone, this is the complete source code for my game Cars2048 (Android, 2017, Unity3D)...]]></summary></entry><entry><title type="html">My blog is turning 10 years old today!</title><link href="http://damienfremont.com/2022/02/20/my-blog-is-turning-10-years-old-today" rel="alternate" type="text/html" title="My blog is turning 10 years old today!" /><published>2022-02-20T00:00:00+00:00</published><updated>2022-02-20T00:00:00+00:00</updated><id>http://damienfremont.com/2022/02/20/my-blog-is-turning-10-years-old-today</id><content type="html" xml:base="http://damienfremont.com/2022/02/20/my-blog-is-turning-10-years-old-today"><![CDATA[<p><img src="/upload/10years3.jpg" alt="alt text" /></p>

<p>Today (20 February 2022) is a very meaningful and historic day for me as it marks the 10th anniversary of my blog here. A decade has gone since I browse through the web looking for a platform for blogging and then decided to join WordPress after Blogger.</p>

<h2 id="what-ive-learned-">What I’ve Learned ?</h2>

<p>This blog helped me a lot in my job. Motivate myself to learn on my own, then sharing knowledge with others.</p>

<p><img src="/upload/blogging-083016.png" alt="alt text" /></p>

<h2 id="whats-next-">What’s Next ?</h2>

<p>More products and less blogging!</p>

<p><img src="/upload/low-code-guide-pro-dev.png" alt="alt text" /></p>

<h2 id="last-word">Last word</h2>

<p>Last but not least, Happy 10th Birthday to my blog once again!</p>

<p>Thank you all.</p>

<p><img src="/upload/212323546ba-colorful-fireworks-animated-gif-pic.gif" alt="alt text" /></p>]]></content><author><name>Damien Fremont</name></author><category term="other" /><summary type="html"><![CDATA[A decade has gone since I browse through the web looking for a platform for blogging and then decided to join WordPress after Blogge...]]></summary></entry><entry><title type="html">How-to JUnit: Test Driven Development / TDD In Java (Step by step with Helloworld Examples)</title><link href="http://damienfremont.com/2021/02/18/test-driven-development-tdd-in-java-helloworld-examples" rel="alternate" type="text/html" title="How-to JUnit: Test Driven Development / TDD In Java (Step by step with Helloworld Examples)" /><published>2021-02-18T00:00:00+00:00</published><updated>2021-02-18T00:00:00+00:00</updated><id>http://damienfremont.com/2021/02/18/test-driven-development-tdd-in-java-helloworld-examples</id><content type="html" xml:base="http://damienfremont.com/2021/02/18/test-driven-development-tdd-in-java-helloworld-examples"><![CDATA[<p><img src="/upload/post-thumbnail-tdd.png" alt="post-thumbnail" /></p>

<p>Test-driven development is not about testing. Test-driven development is about development (and design), specifically improving the quality and design of code. The resulting unit tests are just an extremely useful by-product.</p>

<hr />

<h3 id="tdd-life-cycle">TDD life-cycle</h3>

<p>Before explaining best practices, it is important to understand the TDD life-cycle.</p>
<ul>
  <li>Write the test</li>
  <li>Run the test (there is no implementation code, test does not pass)</li>
  <li>Write just enough implementation code to make the test pass</li>
  <li>Run all tests (tests pass)</li>
  <li>Refactor</li>
  <li>Repeat</li>
</ul>

<p><img src="/upload/README-tddillustration.png" alt="post-image" /></p>

<hr />

<h2 id="result">Result</h2>

<p><code class="language-plaintext highlighter-rouge">HelloWorldTest.java</code></p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit5Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.api.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.params.ParameterizedTest</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.params.provider.CsvSource</span><span class="o">;</span>

<span class="nd">@DisplayNameGeneration</span><span class="o">(</span><span class="nc">DisplayNameGenerator</span><span class="o">.</span><span class="na">ReplaceUnderscores</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld6FinalTest</span> <span class="kd">extends</span> <span class="nc">JUnit5Parent</span> <span class="o">{</span>

    <span class="nd">@Test</span>
    <span class="kt">void</span> <span class="nf">print_Hello_World_by_default</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">String</span> <span class="n">expected</span> <span class="o">=</span> <span class="s">"Hello, World!!"</span><span class="o">;</span>
        <span class="nc">HelloWorld6Final</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
        <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"print Hello with parameter (expected, parameter)"</span><span class="o">)</span>
    <span class="nd">@ParameterizedTest</span>
    <span class="nd">@CsvSource</span><span class="o">({</span>
            <span class="s">"'Hello, Damien!', Damien"</span><span class="o">,</span>
            <span class="s">"'Hello, Jane!', Jane"</span><span class="o">,</span>
            <span class="s">"'Hello, John!', John"</span><span class="o">,</span>
    <span class="o">})</span>
    <span class="kt">void</span> <span class="nf">print_Hello_with_parameter</span><span class="o">(</span><span class="nc">String</span> <span class="n">expected</span><span class="o">,</span> <span class="nc">String</span> <span class="n">parameter</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">String</span><span class="o">[]</span> <span class="n">args</span> <span class="o">=</span> <span class="o">{</span><span class="n">parameter</span><span class="o">};</span>
        <span class="nc">HelloWorld6Final</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
        <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"exception bad format (description, expected, parameter)"</span><span class="o">)</span>
    <span class="nd">@ParameterizedTest</span>
    <span class="nd">@CsvSource</span><span class="o">({</span>
            <span class="s">"Param is empty,  bad arg (ex: Damien), ''  "</span><span class="o">,</span>
            <span class="s">"Param is blanck, bad arg (ex: Damien), ' ' "</span><span class="o">,</span>
            <span class="s">"Param is null,   bad arg (ex: Damien), null"</span>
    <span class="o">})</span>
    <span class="kt">void</span> <span class="nf">exception_bad_format</span><span class="o">(</span><span class="nc">String</span> <span class="n">description</span><span class="o">,</span> <span class="nc">String</span> <span class="n">expected</span><span class="o">,</span> <span class="nc">String</span> <span class="n">parameter</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">String</span><span class="o">[]</span> <span class="n">args</span> <span class="o">=</span> <span class="o">{</span><span class="n">parameter</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="s">"null"</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">parameter</span><span class="o">};</span>
        <span class="nc">Exception</span> <span class="n">e</span> <span class="o">=</span> <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertThrows</span><span class="o">(</span><span class="nc">IllegalArgumentException</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span>
            <span class="nc">HelloWorld6Final</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
        <span class="o">});</span>
        <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">e</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<hr />

<h2 id="specifications">Specifications</h2>

<p>This tutorial will implement the following specifications.</p>

<ul>
  <li>Print “Hello, World!”</li>
  <li>Print “Hello, <MY NAME="">!", where <MY NAME=""> is a parameter</MY></MY></li>
  <li>If parameter is not present, Print “Hello, World!”</li>
  <li>If parameter is not null or empty, then exception “bad arg (ex: Damien)”</li>
</ul>

<hr />

<h2 id="steps">Steps</h2>

<p>Order is important, next step is also solution to previous step.
(in Git Projet, Just follow classes named <code class="language-plaintext highlighter-rouge">HelloWorld*Test</code> order)</p>

<ol>
  <li>Start by developing <code class="language-plaintext highlighter-rouge">Print "Hello, World!"</code>
    <ol>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#print_Hello_World</code> Complete the test</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#print_Hello_World</code> Run the test (test does not pass)</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorld</code> Write just enough implementation code to make the test pass</li>
    </ol>
  </li>
  <li>Repeat with <code class="language-plaintext highlighter-rouge">Print "Hello, &lt;MY NAME&gt;!", where &lt;MY NAME&gt; is a parameter</code>
    <ol>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#print_Hello_with_parameter</code> Complete the test</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#print_Hello_with_parameter</code> Run the test (test does not pass)</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorld</code> Write just enough implementation code to make the test pass</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#*</code> Run all tests (tests pass)</li>
    </ol>
  </li>
  <li>Repeat with <code class="language-plaintext highlighter-rouge">If parameter is not present, Print "Hello, World!"</code>
    <ol>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#print_Hello_World_by_default</code> Complete the test</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#print_Hello_World_by_default</code> Run the test (test does not pass)</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorld</code> Write just enough implementation code to make the test pass</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#*</code> Run all tests (tests pass)</li>
    </ol>
  </li>
  <li>Refactor
    <ol>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest</code> Migrate to JUnit5</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorld</code> Use Ternary operator for inline condition</li>
    </ol>
  </li>
  <li>Repeat with <code class="language-plaintext highlighter-rouge">If parameter is not null or empty, then exception "bad arg (ex: Damien)"</code>
    <ol>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#exception_bad_format</code> Complete the test</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#exception_bad_format</code> Run the test (test does not pass)</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorld</code> Write just enough implementation code to make the test pass</li>
      <li><code class="language-plaintext highlighter-rouge">HelloWorldTest#*</code> Run all tests (tests pass)</li>
    </ol>
  </li>
</ol>

<hr />

<h3 id="step-0---initial-commit">Step 0 - Initial commit</h3>

<p>HelloWorld.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="o">{</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// TODO: NEXT: Print "Hello, World!"</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<hr />

<h3 id="step-1">Step 1</h3>

<p>Start by developing <code class="language-plaintext highlighter-rouge">Print "Hello, World!"</code></p>

<p>HelloWorldTest.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit4Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Test</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorldTest</span> <span class="kd">extends</span> <span class="nc">JUnit4Parent</span> <span class="o">{</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print_Hello_World</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalStateException</span><span class="o">(</span><span class="s">"Not yet implemented!"</span><span class="o">);</span>
        <span class="c1">// TODO: NEXT OUTPUT: super.getOutput()</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Run the test (test does not pass)</p>

<p><img src="/upload/README-testreport-helloworld0-fail.png" alt="post-image" /></p>

<p>Complete the test</p>

<p>HelloWorldTest.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit4Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Assert</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Test</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld1Test</span> <span class="kd">extends</span> <span class="nc">JUnit4Parent</span> <span class="o">{</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print_Hello_World</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">String</span> <span class="n">expected</span> <span class="o">=</span> <span class="s">"Hello, World!"</span><span class="o">;</span>
        <span class="nc">HelloWorld1</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
        <span class="nc">Assert</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Run the test (test does not pass)</p>

<p><img src="/upload/README-testreport-helloworld1-fail.png" alt="post-image" /></p>

<p>Write just enough implementation code to make the test pass</p>

<p>HelloWorld.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld1</span> <span class="o">{</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">String</span> <span class="n">text</span> <span class="o">=</span> <span class="s">"Hello, World!"</span><span class="o">;</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">text</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p><img src="/upload/README-testreport-helloworld1-ok.png" alt="post-image" /></p>

<hr />

<h3 id="step-2">Step 2</h3>

<p>Repeat with <code class="language-plaintext highlighter-rouge">Print "Hello, &lt;MY NAME&gt;!", where &lt;MY NAME&gt; is a parameter</code></p>

<p>HelloWorldTest.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit4Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Assert</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Test</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld2Test</span> <span class="kd">extends</span> <span class="nc">JUnit4Parent</span> <span class="o">{</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print_Hello_World</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">String</span> <span class="n">expected</span> <span class="o">=</span> <span class="s">"Hello, World!"</span><span class="o">;</span>
        <span class="nc">HelloWorld2</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
        <span class="nc">Assert</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print_Hello_with_parameter</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalStateException</span><span class="o">(</span><span class="s">"Not yet implemented!"</span><span class="o">);</span>
        <span class="c1">// TODO: NEXT: Print "Hello, &lt;MY NAME&gt;!", where &lt;MY NAME&gt; is a parameter</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Complete the test</p>

<p>HelloWorldTest.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit4Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Assert</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.runner.RunWith</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.runners.Parameterized</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.Arrays</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Collection</span><span class="o">;</span>

<span class="nd">@RunWith</span><span class="o">(</span><span class="nc">Parameterized</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld2Test</span> <span class="kd">extends</span> <span class="nc">JUnit4Parent</span> <span class="o">{</span>

    <span class="nd">@Parameterized</span><span class="o">.</span><span class="na">Parameters</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">Collection</span><span class="o">&lt;</span><span class="nc">Object</span><span class="o">[]&gt;</span> <span class="nf">data</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="k">new</span> <span class="nc">Object</span><span class="o">[][]{</span>
                <span class="o">{</span><span class="s">"Hello, World!"</span><span class="o">,</span> <span class="s">"World"</span><span class="o">},</span>
                <span class="o">{</span><span class="s">"Hello, Damien!"</span><span class="o">,</span> <span class="s">"Damien"</span><span class="o">},</span>
                <span class="o">{</span><span class="s">"Hello, Jane!"</span><span class="o">,</span> <span class="s">"Jane"</span><span class="o">},</span>
                <span class="o">{</span><span class="s">"Hello, John!"</span><span class="o">,</span> <span class="s">"John"</span><span class="o">},</span>
        <span class="o">});</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">expected</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">first</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">HelloWorld2Test</span><span class="o">(</span><span class="nc">String</span> <span class="n">expected</span><span class="o">,</span> <span class="nc">String</span> <span class="n">first</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">first</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">expected</span> <span class="o">=</span> <span class="n">expected</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print_Hello_with_parameter</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">String</span><span class="o">[]</span> <span class="n">args</span> <span class="o">=</span> <span class="o">{</span><span class="n">first</span><span class="o">};</span>
        <span class="nc">HelloWorld2</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
        <span class="nc">Assert</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Run the test (test does not pass)</p>

<p><img src="/upload/README-testreport-helloworld2-fail.png" alt="post-image" /></p>

<p>Write just enough implementation code to make the test pass</p>

<p>HelloWorld.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld2</span> <span class="o">{</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">String</span> <span class="n">text</span> <span class="o">=</span> <span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"Hello, %s!"</span><span class="o">,</span> <span class="n">text</span><span class="o">));</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p><img src="/upload/README-testreport-helloworld2-ok.png" alt="post-image" /></p>

<hr />

<h3 id="step-3">Step 3</h3>

<p>Repeat with <code class="language-plaintext highlighter-rouge">If parameter is not present, Print "Hello, World!"</code></p>

<p>HelloWorldTest.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit4Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Assert</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.runner.RunWith</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.runners.Parameterized</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.Arrays</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Collection</span><span class="o">;</span>

<span class="nd">@RunWith</span><span class="o">(</span><span class="nc">Parameterized</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld3Test</span> <span class="kd">extends</span> <span class="nc">JUnit4Parent</span> <span class="o">{</span>

    <span class="nd">@Parameterized</span><span class="o">.</span><span class="na">Parameters</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">Collection</span><span class="o">&lt;</span><span class="nc">Object</span><span class="o">[]&gt;</span> <span class="nf">data</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="k">new</span> <span class="nc">Object</span><span class="o">[][]{</span>
                <span class="o">{</span><span class="s">"Hello, World!"</span><span class="o">,</span> <span class="s">"World"</span><span class="o">},</span>
                <span class="o">{</span><span class="s">"Hello, Damien!"</span><span class="o">,</span> <span class="s">"Damien"</span><span class="o">},</span>
                <span class="o">{</span><span class="s">"Hello, Jane!"</span><span class="o">,</span> <span class="s">"Jane"</span><span class="o">},</span>
                <span class="o">{</span><span class="s">"Hello, John!"</span><span class="o">,</span> <span class="s">"John"</span><span class="o">},</span>
        <span class="o">});</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">expected</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">first</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">HelloWorld3Test</span><span class="o">(</span><span class="nc">String</span> <span class="n">expected</span><span class="o">,</span> <span class="nc">String</span> <span class="n">first</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">first</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">expected</span> <span class="o">=</span> <span class="n">expected</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print_Hello_with_parameter</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">String</span><span class="o">[]</span> <span class="n">args</span> <span class="o">=</span> <span class="o">{</span><span class="n">first</span><span class="o">};</span>
        <span class="nc">HelloWorld3</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
        <span class="nc">Assert</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print_Hello_World_by_default</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalStateException</span><span class="o">(</span><span class="s">"Not yet implemented!"</span><span class="o">);</span>
        <span class="c1">// TODO: NEXT: If parameter is not present, Print "Hello, World!"</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Complete the test</p>

<p>HelloWorldTest.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit4Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Assert</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.Test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.runner.RunWith</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.runners.Parameterized</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.Arrays</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Collection</span><span class="o">;</span>

<span class="nd">@RunWith</span><span class="o">(</span><span class="nc">Parameterized</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld3Test</span> <span class="kd">extends</span> <span class="nc">JUnit4Parent</span> <span class="o">{</span>

    <span class="nd">@Parameterized</span><span class="o">.</span><span class="na">Parameters</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">Collection</span><span class="o">&lt;</span><span class="nc">Object</span><span class="o">[]&gt;</span> <span class="nf">data</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="k">new</span> <span class="nc">Object</span><span class="o">[][]{</span>
                <span class="o">{</span><span class="s">"Hello, World!"</span><span class="o">,</span> <span class="s">"World"</span><span class="o">},</span>
                <span class="o">{</span><span class="s">"Hello, Damien!"</span><span class="o">,</span> <span class="s">"Damien"</span><span class="o">},</span>
                <span class="o">{</span><span class="s">"Hello, Jane!"</span><span class="o">,</span> <span class="s">"Jane"</span><span class="o">},</span>
                <span class="o">{</span><span class="s">"Hello, John!"</span><span class="o">,</span> <span class="s">"John"</span><span class="o">},</span>
        <span class="o">});</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">expected</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">first</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">HelloWorld3Test</span><span class="o">(</span><span class="nc">String</span> <span class="n">expected</span><span class="o">,</span> <span class="nc">String</span> <span class="n">first</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">first</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">expected</span> <span class="o">=</span> <span class="n">expected</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print_Hello_with_parameter</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">String</span><span class="o">[]</span> <span class="n">args</span> <span class="o">=</span> <span class="o">{</span><span class="n">first</span><span class="o">};</span>
        <span class="nc">HelloWorld3</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
        <span class="nc">Assert</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="nd">@Test</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print_Hello_World_by_default</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">String</span> <span class="n">expected</span> <span class="o">=</span> <span class="s">"Hello, World!!"</span><span class="o">;</span>
        <span class="nc">HelloWorld4</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
        <span class="nc">Assert</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Run the test (test does not pass)</p>

<p><img src="/upload/README-testreport-helloworld2-fail.png" alt="post-image" /></p>

<p>Write just enough implementation code to make the test pass</p>

<p>HelloWorld.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld3</span> <span class="o">{</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// TODO: NEXT: If parameter is not present, Print "Hello, World!"</span>
        <span class="nc">String</span> <span class="n">text</span> <span class="o">=</span> <span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"Hello, %s!"</span><span class="o">,</span> <span class="n">text</span><span class="o">));</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p><img src="/upload/README-testreport-helloworld3-ok.png" alt="post-image" /></p>

<hr />

<h3 id="step-4">Step 4</h3>

<p>Refactor from JUnit4 to JUnit5</p>

<p>HelloWorldTest.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit5Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.api.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.params.ParameterizedTest</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.params.provider.CsvSource</span><span class="o">;</span>

<span class="nd">@DisplayNameGeneration</span><span class="o">(</span><span class="nc">DisplayNameGenerator</span><span class="o">.</span><span class="na">ReplaceUnderscores</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld5Test</span> <span class="kd">extends</span> <span class="nc">JUnit5Parent</span> <span class="o">{</span>

   <span class="nd">@Test</span>
   <span class="kt">void</span> <span class="nf">print_Hello_World_by_default</span><span class="o">()</span> <span class="o">{</span>
      <span class="nc">String</span> <span class="n">expected</span> <span class="o">=</span> <span class="s">"Hello, World!!"</span><span class="o">;</span>
      <span class="nc">HelloWorld5</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
      <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
   <span class="o">}</span>

   <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"print Hello with parameter (expected, parameter)"</span><span class="o">)</span>
   <span class="nd">@ParameterizedTest</span>
   <span class="nd">@CsvSource</span><span class="o">({</span>
           <span class="s">"'Hello, Damien!', Damien"</span><span class="o">,</span>
           <span class="s">"'Hello, Jane!', Jane"</span><span class="o">,</span>
           <span class="s">"'Hello, John!', John"</span><span class="o">,</span>
   <span class="o">})</span>
   <span class="kt">void</span> <span class="nf">print_Hello_with_parameter</span><span class="o">(</span><span class="nc">String</span> <span class="n">expected</span><span class="o">,</span> <span class="nc">String</span> <span class="n">parameter</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">String</span><span class="o">[]</span> <span class="n">args</span> <span class="o">=</span> <span class="o">{</span><span class="n">parameter</span><span class="o">};</span>
      <span class="nc">HelloWorld5</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
      <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
   <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>HelloWorld.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld5</span> <span class="o">{</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// TODO: NEXT: `If parameter is not null or empty, then exception "bad arg (ex: Damien)"`</span>
        <span class="nc">String</span> <span class="n">text</span> <span class="o">=</span> <span class="n">hasNoArgs</span><span class="o">(</span><span class="n">args</span><span class="o">)</span> <span class="o">?</span> <span class="s">"World!"</span> <span class="o">:</span> <span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"Hello, %s!"</span><span class="o">,</span> <span class="n">text</span><span class="o">));</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">boolean</span> <span class="nf">hasNoArgs</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">args</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="o">(</span><span class="n">args</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">args</span><span class="o">.</span><span class="na">length</span> <span class="o">==</span> <span class="mi">0</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Run all tests (tests pass)</p>

<p><img src="/upload/README-testreport-helloworld3-ok.png" alt="post-image" /></p>

<hr />

<h3 id="step-5">Step 5</h3>

<p>Repeat with <code class="language-plaintext highlighter-rouge">If parameter is not null or empty, then exception "bad arg (ex: Damien)"</code></p>

<p>HelloWorldTest.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit5Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.api.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.params.ParameterizedTest</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.params.provider.CsvSource</span><span class="o">;</span>

<span class="nd">@DisplayNameGeneration</span><span class="o">(</span><span class="nc">DisplayNameGenerator</span><span class="o">.</span><span class="na">ReplaceUnderscores</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld5Test</span> <span class="kd">extends</span> <span class="nc">JUnit5Parent</span> <span class="o">{</span>

   <span class="nd">@Test</span>
   <span class="kt">void</span> <span class="nf">print_Hello_World_by_default</span><span class="o">()</span> <span class="o">{</span>
      <span class="nc">String</span> <span class="n">expected</span> <span class="o">=</span> <span class="s">"Hello, World!!"</span><span class="o">;</span>
      <span class="nc">HelloWorld5</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
      <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
   <span class="o">}</span>

   <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"print Hello with parameter (expected, parameter)"</span><span class="o">)</span>
   <span class="nd">@ParameterizedTest</span>
   <span class="nd">@CsvSource</span><span class="o">({</span>
           <span class="s">"'Hello, Damien!', Damien"</span><span class="o">,</span>
           <span class="s">"'Hello, Jane!', Jane"</span><span class="o">,</span>
           <span class="s">"'Hello, John!', John"</span><span class="o">,</span>
   <span class="o">})</span>
   <span class="kt">void</span> <span class="nf">print_Hello_with_parameter</span><span class="o">(</span><span class="nc">String</span> <span class="n">expected</span><span class="o">,</span> <span class="nc">String</span> <span class="n">parameter</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">String</span><span class="o">[]</span> <span class="n">args</span> <span class="o">=</span> <span class="o">{</span><span class="n">parameter</span><span class="o">};</span>
      <span class="nc">HelloWorld5</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
      <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
   <span class="o">}</span>

   <span class="nd">@Test</span>
   <span class="kt">void</span> <span class="nf">exception_bad_format</span><span class="o">()</span> <span class="o">{</span>
      <span class="c1">// TODO: NEXT: `If parameter is not null or empty, then exception "bad arg (ex: Damien)"`</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalStateException</span><span class="o">(</span><span class="s">"Not yet implemented!"</span><span class="o">);</span>
   <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Complete the test</p>

<p>HelloWorldTest.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">com.damienfremont.blog.utils.JUnit5Parent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.api.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.params.ParameterizedTest</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.junit.jupiter.params.provider.CsvSource</span><span class="o">;</span>

<span class="nd">@DisplayNameGeneration</span><span class="o">(</span><span class="nc">DisplayNameGenerator</span><span class="o">.</span><span class="na">ReplaceUnderscores</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld6FinalTest</span> <span class="kd">extends</span> <span class="nc">JUnit5Parent</span> <span class="o">{</span>

    <span class="nd">@Test</span>
    <span class="kt">void</span> <span class="nf">print_Hello_World_by_default</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">String</span> <span class="n">expected</span> <span class="o">=</span> <span class="s">"Hello, World!!"</span><span class="o">;</span>
        <span class="nc">HelloWorld6Final</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
        <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"print Hello with parameter (expected, parameter)"</span><span class="o">)</span>
    <span class="nd">@ParameterizedTest</span>
    <span class="nd">@CsvSource</span><span class="o">({</span>
            <span class="s">"'Hello, Damien!', Damien"</span><span class="o">,</span>
            <span class="s">"'Hello, Jane!', Jane"</span><span class="o">,</span>
            <span class="s">"'Hello, John!', John"</span><span class="o">,</span>
    <span class="o">})</span>
    <span class="kt">void</span> <span class="nf">print_Hello_with_parameter</span><span class="o">(</span><span class="nc">String</span> <span class="n">expected</span><span class="o">,</span> <span class="nc">String</span> <span class="n">parameter</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">String</span><span class="o">[]</span> <span class="n">args</span> <span class="o">=</span> <span class="o">{</span><span class="n">parameter</span><span class="o">};</span>
        <span class="nc">HelloWorld6Final</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
        <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">getOutput</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"exception bad format (description, expected, parameter)"</span><span class="o">)</span>
    <span class="nd">@ParameterizedTest</span>
    <span class="nd">@CsvSource</span><span class="o">({</span>
            <span class="s">"Param is empty,  bad arg (ex: Damien), ''  "</span><span class="o">,</span>
            <span class="s">"Param is blanck, bad arg (ex: Damien), ' ' "</span><span class="o">,</span>
            <span class="s">"Param is null,   bad arg (ex: Damien), null"</span>
    <span class="o">})</span>
    <span class="kt">void</span> <span class="nf">exception_bad_format</span><span class="o">(</span><span class="nc">String</span> <span class="n">description</span><span class="o">,</span> <span class="nc">String</span> <span class="n">expected</span><span class="o">,</span> <span class="nc">String</span> <span class="n">parameter</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">String</span><span class="o">[]</span> <span class="n">args</span> <span class="o">=</span> <span class="o">{</span><span class="n">parameter</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="s">"null"</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">parameter</span><span class="o">};</span>
        <span class="nc">Exception</span> <span class="n">e</span> <span class="o">=</span> <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertThrows</span><span class="o">(</span><span class="nc">IllegalArgumentException</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span>
            <span class="nc">HelloWorld6Final</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
        <span class="o">});</span>
        <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="n">expected</span><span class="o">,</span> <span class="n">e</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Run the test (test does not pass)</p>

<p><img src="/upload/README-testreport-helloworld5-fail.png" alt="post-image" /></p>

<p>HelloWorld.java</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.damienfremont.blog</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld6Final</span> <span class="o">{</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">String</span> <span class="n">name</span> <span class="o">=</span> <span class="n">getName</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
        <span class="nc">String</span> <span class="n">text</span> <span class="o">=</span> <span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"Hello, %s!"</span><span class="o">,</span> <span class="n">name</span><span class="o">);</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">text</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">getName</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">hasNoArgs</span><span class="o">(</span><span class="n">args</span><span class="o">))</span> <span class="o">{</span>
            <span class="k">return</span> <span class="s">"World!"</span><span class="o">;</span>
        <span class="o">}</span>
        <span class="k">if</span><span class="o">(</span><span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">]))</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"bad arg (ex: Damien)"</span><span class="o">);</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">boolean</span> <span class="nf">isNullOrEmpty</span><span class="o">(</span><span class="nc">String</span> <span class="n">arg</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">arg</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">arg</span><span class="o">.</span><span class="na">trim</span><span class="o">().</span><span class="na">isEmpty</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kt">boolean</span> <span class="nf">hasNoArgs</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">args</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="o">(</span><span class="n">args</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">args</span><span class="o">.</span><span class="na">length</span> <span class="o">==</span> <span class="mi">0</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Run all tests (tests pass)</p>

<p><img src="/upload/README-testreport-helloworld5-ok.png" alt="post-image" /></p>

<p>And it’s finished !!!</p>

<hr />

<h2 id="conclusion">Conclusion</h2>

<p>Test-driven development is not about testing.</p>

<p>Test-driven development is about development (and design), specifically improving the quality and design of code. The resulting unit tests are just an extremely useful by-product.</p>

<hr />

<h2 id="post">Post</h2>

<ul>
  <li><a href="https://damienfremont.com/2021/02/18/test-driven-development-tdd-in-java-helloworld-examples">https://damienfremont.com/2021/02/18/test-driven-development-tdd-in-java-helloworld-examples/</a></li>
</ul>

<h2 id="source">Source</h2>

<ul>
  <li><a href="https://github.com/DamienFremont/damienfremont.com-blog-labs/tree/master/20210201-test-tdd-java-helloworld-examples">https://github.com/DamienFremont/damienfremont.com-blog-labs/tree/master/20210201-test-tdd-java-helloworld-examples</a></li>
</ul>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://javacodehouse.com/blog/test-driven-development-tutorial/#what-is-tdd">https://javacodehouse.com/blog/test-driven-development-tutorial/#what-is-tdd</a></li>
  <li><a href="https://technologyconversations.com/2014/09/30/test-driven-development-tdd/">https://technologyconversations.com/2014/09/30/test-driven-development-tdd/</a></li>
  <li><a href="https://technologyconversations.com/2013/12/24/test-driven-development-tdd-best-practices-using-java-examples-2/">https://technologyconversations.com/2013/12/24/test-driven-development-tdd-best-practices-using-java-examples-2/</a></li>
</ul>]]></content><author><name>Damien Fremont</name></author><category term="top" /><category term="top5" /><category term="topjava" /><category term="java" /><category term="junit" /><category term="spring" /><category term="tdd" /><category term="test" /><summary type="html"><![CDATA[Test-driven development is not about testing. Test-driven development is about development (and design), specifically improving the quality and design of code...]]></summary></entry><entry><title type="html">Software Architecture – “Train wagons” – Be agile and prioritize your stack</title><link href="http://damienfremont.com/2020/07/05/software-architecture-train-wagons-be-agile-and-prioritize-your-stack" rel="alternate" type="text/html" title="Software Architecture – “Train wagons” – Be agile and prioritize your stack" /><published>2020-07-05T00:00:00+00:00</published><updated>2020-07-05T00:00:00+00:00</updated><id>http://damienfremont.com/2020/07/05/software-architecture-train-wagons-be-agile-and-prioritize-your-stack</id><content type="html" xml:base="http://damienfremont.com/2020/07/05/software-architecture-train-wagons-be-agile-and-prioritize-your-stack"><![CDATA[<p><img src="/upload/2020-07-05-diapositive2-copy.jpg" alt="post-image" /></p>

<p>You can view an application as a train, and components as wagons. It’s possible to let go some of them if needed, like features for a project backlog. But here, instead of saving now on time or cost (implementation), you mostly save on future cost (maintenance).</p>

<h2 id="content">Content:</h2>

<ol>
  <li>“Train Wagons” Metaphor</li>
  <li>Example – Classic Full-stack Javascript Project</li>
  <li>Step 1 – Prioritize Stack</li>
  <li>Step 2 – Identify non-essential or non-LTS parts</li>
  <li>Step 3 – Repeat until best-value is achieved</li>
</ol>

<hr />

<h2 id="the-train-wagons-metaphor">The “Train Wagons” Metaphor</h2>

<p>You can view tasks in a list, as wagons attached to a train. First wagon is the most important (a must have), last wagon is the least important (nice to have).</p>

<p><img src="/upload/2020-07-05-Diapositive1.JPG" alt="post-image" /></p>

<p>…and when you can’t make it to the end in the required budget or time, you could let go some wagons from the tail (actual benefice).</p>

<p><img src="/upload/2020-07-05-Diapositive3.JPG" alt="post-image" /></p>

<p>It’s also possible to view an application stack, or software architecture, in the same way. The train is the application, parts of it are wagons and you can let go some wagons. But here, instead of saving on time, you save on maintenance cost (future benefice).</p>

<p><img src="/upload/2020-07-05-Diapositive2.JPG" alt="post-image" /></p>

<h2 id="example--classic-full-stack-javascript-project">Example – Classic Full-stack Javascript Project</h2>

<p>Here is a simple example, a Single Page App (SPA) using classic stack from 2019: a server running NodeJS, PostgreSQL, and also a browser like Firefox executing ReactJS, Bootstrap.</p>

<p><img src="/upload/2020-07-05-README-screenshot.png" alt="post-image" /></p>

<p><img src="/upload/2020-07-05-README-tech-stack.png" alt="post-image" /></p>

<p>It’s obvious that’s not enough to build your project. You also need third-part library to build on top of your main components, like ExpressJS for NodeJS, and tools or even a new language, like TypeScript for JavaScript.</p>

<p><img src="/upload/2020-07-05-README-tech-architecture.png" alt="post-image" /></p>

<h2 id="step-1--prioritize-stack">Step 1 – Prioritize Stack</h2>

<p>Not all components are equals. You need to identify which depends on which (A -&gt; B -&gt; C ..to show that A is the main component, and B and C some add-ons to it)</p>

<p>The classic way is to group them by software layers, then do the dependency graph. Usually with full-stack applications, you’ll find 2 layers (back-end server, database), up to sometimes 4 layers (+proxy, +front-end).</p>

<p><img src="/upload/2020-07-05-Diapositive4.JPG" alt="post-image" /></p>

<p>In this diagram, there are two layers: front-end (browser) and back-end (server). It’s obvious that the front-end is kinda bloated, specifically with add-ons!</p>

<h2 id="step-2--identify-non-essential-or-non-lts-parts">Step 2 – Identify non-essential or non-LTS parts</h2>

<p>There are multiple category for identifying non-essential parts, depending on your goals and constraints (team size, budget). Ex: boilerplate is a problem for every team, but too many tools not so much if your team is large or your environment complex.</p>

<p>Too many tools:  SaSS, you are not FaceBook ..maybe just CSS with some overrides on Bootstrap?</p>

<p>Overkill delivery: Docker for standard application ..maybe just Git pull or Zip transfer?</p>

<p>Heavy boilerplate: small library, only using some functions, that you could write and maintained yourself!</p>

<p>Constant refactoring: library without Long Term Support (LTS) or community (ex: 1 developer)</p>

<p>Over-testing: why doing unit testing (Mocha) on front-end ? when you already do UI testing with full coverage (Selenium, Protractor)</p>

<p><img src="/upload/2020-07-05-Diapositive5.JPG" alt="post-image" /></p>

<p><img src="/upload/2020-07-05-Diapositive6.JPG" alt="post-image" /></p>

<h2 id="step-3--repeat-until-best-value-is-achieved">Step 3 – Repeat until best-value is achieved</h2>

<p>You need to know when to stop.</p>

<p>Client side: maybe your users don’t need it (simple website or business application)</p>

<p>Tests: maybe your tests are bad (low coverage, disabled, unmaintained) or your source code is bullet-proof (generated code, simple), or site downtime is OK (non-critical app).</p>

<p><img src="/upload/2020-07-05-Diapositive7.JPG" alt="post-image" /></p>

<p><img src="/upload/2020-07-05-Diapositive8.JPG" alt="post-image" /></p>

<h2 id="conclusion">Conclusion</h2>

<p>You need to answer these questions: Are all components essentials ? and not just here for the hype ? Do I need a perfect clean project ? What is the future cost of each non-essential components ?</p>

<p><img src="/upload/2020-07-05-Diapositive9.JPG" alt="post-image" /></p>

<h2 id="project-source">Project Source</h2>

<ul>
  <li><a href="https://github.com/DamienFremont/damienfremont.com-blog-labs/tree/master/20200705-design-train-wagons-be-agile-and-prioritize-your-stack">https://github.com/DamienFremont/damienfremont.com-blog-labs/tree/master/20200705-design-train-wagons-be-agile-and-prioritize-your-stack</a></li>
</ul>

<h2 id="resources">Resources</h2>

<ul>
  <li>Wikipedia
    <ul>
      <li><a href="https://en.wikipedia.org/wiki/Software_architecture">Software_architecture</a></li>
      <li><a href="https://en.wikipedia.org/wiki/MoSCoW_method">MoSCoW_method</a></li>
    </ul>
  </li>
</ul>]]></content><author><name>Damien Fremont</name></author><category term="top" /><category term="topdesign" /><category term="agile" /><category term="design" /><summary type="html"><![CDATA[You can view an application as a train, and components as wagons. It’s possible to let go some of them if needed, like features for a project backlog...]]></summary></entry><entry><title type="html">How to Photoshop Script: Translate Text Then Save PNG for Each Language</title><link href="http://damienfremont.com/2019/06/20/how-to-photoshop-script-translate-text-then-save-png-for-each-language" rel="alternate" type="text/html" title="How to Photoshop Script: Translate Text Then Save PNG for Each Language" /><published>2019-06-20T00:00:00+00:00</published><updated>2019-06-20T00:00:00+00:00</updated><id>http://damienfremont.com/2019/06/20/how-to-photoshop-script-translate-text-then-save-png-for-each-language</id><content type="html" xml:base="http://damienfremont.com/2019/06/20/how-to-photoshop-script-translate-text-then-save-png-for-each-language"><![CDATA[<p><img src="/upload/example-screenshot007.png" alt="post-image" /></p>

<p>Photoshop script to translate text layers based on language JSON files, then export project to PNG images for each language. Save a lot of time!</p>

<p>Usage: generate screenshots files for your app (ex: Googleplay, etc).</p>

<p>One project is converted to X languages screenshots. With the seconde script, you can also convert each group layer as a separate files (for multi-screenshots project).</p>

<h2 id="getting-started-one-screenshot-by-project">Getting started (one screenshot by project)</h2>

<ul>
  <li>Open test photoshop file <code class="language-plaintext highlighter-rouge">./test/project-to-screenshot.psd</code></li>
  <li>Execute <code class="language-plaintext highlighter-rouge">text-project-translate.bat</code></li>
  <li>Get your results from your <code class="language-plaintext highlighter-rouge">Documents</code> folder</li>
</ul>

<p>NOTE: Edit <code class="language-plaintext highlighter-rouge">output</code> value in <code class="language-plaintext highlighter-rouge">text-project-translate.jsx</code> with you want to change the target folder</p>

<p>NOTE: Edit <code class="language-plaintext highlighter-rouge">project-to-screenshot-fr.json</code> or <code class="language-plaintext highlighter-rouge">project-to-screenshot-en.json</code> content to change translation text.</p>

<p>NOTE: Edit same keys in Photoshop projet AND <code class="language-plaintext highlighter-rouge">*.json</code> files.</p>

<p>NOTE: Use ‘Unicode’ type for universal support (asian and russian fonts).</p>

<h2 id="example--test-with-1-screenshot-project">Example – Test with 1 Screenshot (project)</h2>

<p>text-project-translate.bat</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"C:</span><span class="se">\P</span><span class="s2">rogram Files (x86)</span><span class="se">\P</span><span class="s2">hotoshop</span><span class="se">\P</span><span class="s2">hotoshop.exe"</span> c:<span class="se">\U</span>sers<span class="se">\d</span>amien<span class="se">\g</span>it<span class="se">\p</span>hotoshop-script-text-translator<span class="se">\t</span>ext-project-translate.jsx
</code></pre></div></div>

<p>text-project-translate.jsx</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 * Photoshop script to translate text layers based on lang
 * JSON file, then export project to PNG images for each
 * language.
 *
 * Usage: generate screenshots files for your app (ex:
 * Googleplay, etc).
 *
 * @param langs array of languages, linked to translation
 * files named like '-.json'
 * @param langFallback language to use when json file does
 * not exist.
 * @param (input) Current Photoshop project
 * @param (output) Current Photoshop project
 */</span>
 
<span class="kd">var</span> <span class="nx">langs</span> <span class="o">=</span> <span class="p">[</span><span class="dl">"</span><span class="s2">en</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ja</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ko</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">zh-TW</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">de</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">fr</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">pt</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">es</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">it</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ru</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">hi</span><span class="dl">"</span><span class="p">];</span>
<span class="kd">var</span> <span class="nx">langFallback</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">en</span><span class="dl">"</span><span class="p">;</span>
<span class="c1">// get the path of currently opened photoshop document</span>
<span class="kd">var</span> <span class="nx">output</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">activeDocument</span><span class="p">.</span><span class="nx">path</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">input</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">activeDocument</span><span class="p">.</span><span class="nx">path</span><span class="p">;</span>
 
<span class="cm">/* UTILS *************************************************** */</span>
 
<span class="c1">// Because Photoshop doesn't</span>
<span class="kd">function</span> <span class="nx">JSON_parse</span><span class="p">(</span><span class="nx">fileContent</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">/// Extend script doesn't support JSON. Are you fucking kidding?</span>
    <span class="c1">// obj = JSON.parse(file_to_read .read());</span>
    <span class="c1">// Worry not. We got potentially harmful, yet useful, `eval` function.</span>
    <span class="kd">var</span> <span class="nx">jsonFile</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="dl">"</span><span class="s2">(</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">fileContent</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">)</span><span class="dl">"</span><span class="p">);</span>
    <span class="k">return</span> <span class="nx">jsonFile</span><span class="p">;</span>
<span class="p">}</span>
 
<span class="kd">function</span> <span class="nx">FILE_toString</span><span class="p">(</span><span class="nx">fileToRead</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">fileToRead</span> <span class="o">===</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">File read error:</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">jsonFilePath</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="nx">fileToRead</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">'</span><span class="s1">r</span><span class="dl">'</span><span class="p">);</span>
    <span class="kd">var</span> <span class="nx">fileContent</span> <span class="o">=</span> <span class="nx">fileToRead</span><span class="p">.</span><span class="nx">read</span><span class="p">();</span>
    <span class="nx">fileToRead</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
    <span class="k">return</span> <span class="nx">fileContent</span><span class="p">;</span>
<span class="p">}</span>
 
<span class="kd">function</span> <span class="nx">loadLangFile</span><span class="p">(</span><span class="nx">langKey</span><span class="p">)</span> <span class="p">{</span>
 
<span class="p">}</span>
 
<span class="cm">/* EXECUTION *************************************************** */</span>
 
<span class="c1">//Get the currently opened Photoshop document</span>
<span class="kd">var</span> <span class="nx">doc</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">activeDocument</span><span class="p">;</span>
 
<span class="c1">// get filename without extension</span>
<span class="kd">var</span> <span class="nx">filename</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="nx">filename</span> <span class="o">=</span> <span class="nx">filename</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">filename</span><span class="p">.</span><span class="nx">lastIndexOf</span><span class="p">(</span><span class="dl">"</span><span class="s2">.</span><span class="dl">"</span><span class="p">));</span> <span class="c1">//just add this line to the construction.      </span>
 
<span class="c1">// execute for eqch languages</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">t</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">t</span> <span class="o">&amp;</span><span class="nx">lt</span><span class="p">;</span> <span class="nx">langs</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">t</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">currentLang</span> <span class="o">=</span> <span class="nx">langs</span><span class="p">[</span><span class="nx">t</span><span class="p">];</span>
    <span class="kd">var</span> <span class="nx">fileContent</span><span class="p">;</span>
 
    <span class="c1">// Use absolute path for the JSON file.</span>
    <span class="kd">var</span> <span class="nx">langFilePath</span> <span class="o">=</span> <span class="nx">input</span> <span class="o">+</span> <span class="o">&amp;</span><span class="nx">quot</span><span class="p">;</span><span class="sr">/&amp;quot; + filename + &amp;quot;-&amp;quot; + currentLang + &amp;quot;.json&amp;quot;</span><span class="err">;
</span>    <span class="kd">var</span> <span class="nx">fileToRead</span> <span class="o">=</span> <span class="nx">File</span><span class="p">(</span><span class="nx">langFilePath</span><span class="p">);</span>
    <span class="nx">fileContent</span> <span class="o">=</span> <span class="nx">FILE_toString</span><span class="p">(</span><span class="nx">fileToRead</span><span class="p">);</span>
 
    <span class="c1">// Use default language if file not found!</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">fileContent</span> <span class="o">===</span> <span class="o">&amp;</span><span class="nx">quot</span><span class="p">;</span><span class="o">&amp;</span><span class="nx">quot</span><span class="p">;)</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">langFilePath2</span> <span class="o">=</span> <span class="nx">input</span> <span class="o">+</span> <span class="o">&amp;</span><span class="nx">quot</span><span class="p">;</span><span class="sr">/&amp;quot; + filename + &amp;quot;-&amp;quot; + langFallback + &amp;quot;.json&amp;quot;</span><span class="err">;
</span>        <span class="kd">var</span> <span class="nx">fileToRead2</span> <span class="o">=</span> <span class="nx">File</span><span class="p">(</span><span class="nx">langFilePath2</span><span class="p">);</span>
        <span class="nx">fileContent</span> <span class="o">=</span> <span class="nx">FILE_toString</span><span class="p">(</span><span class="nx">fileToRead2</span><span class="p">);</span>
    <span class="p">}</span>
 
    <span class="kd">var</span> <span class="nx">messages</span> <span class="o">=</span> <span class="nx">JSON_parse</span><span class="p">(</span><span class="nx">fileContent</span><span class="p">);</span>
 
    <span class="c1">// Translate each text layers and sub layers</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span><span class="nx">span</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">mce_SELREST_start</span><span class="dl">"</span> <span class="nx">style</span><span class="o">=</span><span class="dl">"</span><span class="s2">overflow:hidden;line-height:0;</span><span class="dl">"</span><span class="o">&gt;&lt;</span><span class="sr">/span&gt;&amp;lt; doc.layers.length; i++</span><span class="se">)</span><span class="sr"> </span><span class="err">{
</span>        <span class="kd">var</span> <span class="nx">currLayer</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
 
        <span class="c1">// check layer is visible</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">currLayer</span><span class="p">.</span><span class="nx">visible</span> <span class="o">===</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
 
        <span class="c1">// check layer is text</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">currLayer</span><span class="p">.</span><span class="nx">kind</span> <span class="o">!=</span> <span class="nx">LayerKind</span><span class="p">.</span><span class="nx">TEXT</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
 
        <span class="c1">// get translation</span>
        <span class="kd">var</span> <span class="nx">translation</span> <span class="o">=</span> <span class="nx">messages</span><span class="p">[</span><span class="nx">currLayer</span><span class="p">.</span><span class="nx">name</span><span class="p">];</span>
 
        <span class="c1">// check translation founded</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">translation</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
 
        <span class="c1">// set new text</span>
        <span class="nx">currLayer</span><span class="p">.</span><span class="nx">textItem</span><span class="p">.</span><span class="nx">contents</span> <span class="o">=</span> <span class="nx">translation</span><span class="p">;</span>
    <span class="p">}</span>
 
    <span class="c1">// save</span>
    <span class="kd">var</span> <span class="nx">file</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">File</span><span class="p">(</span><span class="nx">output</span> <span class="o">+</span> <span class="o">&amp;</span><span class="nx">quot</span><span class="p">;</span><span class="sr">/&amp;quot; + filename + &amp;quot;-&amp;quot; + currentLang + &amp;quot;.png&amp;quot;</span><span class="se">)</span><span class="err">;
</span>    <span class="kd">var</span> <span class="nx">saveOptions</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PNGSaveOptions</span><span class="p">();</span>
    <span class="nx">doc</span><span class="p">.</span><span class="nx">saveAs</span><span class="p">(</span><span class="nx">file</span><span class="p">,</span> <span class="nx">saveOptions</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">Extension</span><span class="p">.</span><span class="nx">LOWERCASE</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/upload/project-screenshot000.png" alt="post-image" /></p>

<p><img src="/upload/project-screenshot001-1.png" alt="post-image" /></p>

<p><img src="/upload/project-screenshot002.png" alt="post-image" /></p>

<p><img src="/upload/project-screenshot003.png" alt="post-image" /></p>

<p><img src="/upload/project-screenshot004.png" alt="post-image" /></p>

<p><img src="/upload/project-screenshot005.png" alt="post-image" /></p>

<p><img src="/upload/project-screenshot006.png" alt="post-image" /></p>

<h2 id="getting-started-multiple-screenshot-layers-by-project">Getting started (multiple screenshot layers by project)</h2>

<ul>
  <li>Open test photoshop file <code class="language-plaintext highlighter-rouge">./test/layers-to-screenshots.psd</code></li>
  <li>Execute <code class="language-plaintext highlighter-rouge">text-layers-translate.bat</code></li>
  <li>Get your results from your <code class="language-plaintext highlighter-rouge">Documents</code> folder</li>
</ul>

<h2 id="example--test-with-2-screenshot-group">Example – Test with 2 Screenshot (group)</h2>

<p>text-group-translate.bat</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"C:</span><span class="se">\P</span><span class="s2">rogram Files (x86)</span><span class="se">\P</span><span class="s2">hotoshop</span><span class="se">\P</span><span class="s2">hotoshop.exe"</span> c:<span class="se">\U</span>sers<span class="se">\d</span>amien<span class="se">\g</span>it<span class="se">\p</span>hotoshop-script-text-translator<span class="se">\t</span>ext-groups-translate.jsx
</code></pre></div></div>

<p>text-groups-translate.jsx</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 * Photoshop script to translate text layers based on lang
 * JSON file, then export project to PNG images for each
 * language.
 *
 * Usage: generate screenshots files for your app (ex:
 * Googleplay, etc).
 *
 * @param langs array of languages, linked to translation
 * files named like '-.json'
 * @param langFallback language to use when json file does
 * not exist.
 * @param (input) Current Photoshop project
 * @param (output) Current Photoshop project
 */</span>
 
<span class="kd">var</span> <span class="nx">langs</span> <span class="o">=</span> <span class="p">[</span><span class="dl">"</span><span class="s2">en</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ja</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ko</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">zh-TW</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">de</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">fr</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">pt</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">es</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">it</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ru</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">hi</span><span class="dl">"</span><span class="p">];</span>
<span class="kd">var</span> <span class="nx">langFallback</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">en</span><span class="dl">"</span><span class="p">;</span>
<span class="c1">// get the path of currently opened photoshop document</span>
<span class="kd">var</span> <span class="nx">inputPath</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">activeDocument</span><span class="p">.</span><span class="nx">path</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">outputPath</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">activeDocument</span><span class="p">.</span><span class="nx">path</span><span class="p">;</span>
 
<span class="cm">/* UTILS *************************************************** */</span>
 
<span class="c1">// Because Photoshop doesn't</span>
<span class="kd">function</span> <span class="nx">JSON_parse</span><span class="p">(</span><span class="nx">fileContent</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">/// Extend script doesn't support JSON. Are you fucking kidding?</span>
    <span class="c1">// obj = JSON.parse(file_to_read .read());</span>
    <span class="c1">// Worry not. We got potentially harmful, yet useful, `eval` function.</span>
    <span class="kd">var</span> <span class="nx">jsonFile</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="dl">"</span><span class="s2">(</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">fileContent</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">)</span><span class="dl">"</span><span class="p">);</span>
    <span class="k">return</span> <span class="nx">jsonFile</span><span class="p">;</span>
<span class="p">}</span>
 
<span class="kd">function</span> <span class="nx">FILE_toString</span><span class="p">(</span><span class="nx">fileToRead</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">fileToRead</span> <span class="o">===</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">File read error:</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">jsonFilePath</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="nx">fileToRead</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">'</span><span class="s1">r</span><span class="dl">'</span><span class="p">);</span>
    <span class="kd">var</span> <span class="nx">fileContent</span> <span class="o">=</span> <span class="nx">fileToRead</span><span class="p">.</span><span class="nx">read</span><span class="p">();</span>
    <span class="nx">fileToRead</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
    <span class="k">return</span> <span class="nx">fileContent</span><span class="p">;</span>
<span class="p">}</span>
 
<span class="cm">/* EXECUTION *************************************************** */</span>
 
<span class="c1">//Get the currently opened Photoshop document</span>
<span class="kd">var</span> <span class="nx">doc</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">activeDocument</span><span class="p">;</span>
 
<span class="c1">// get filename without extension</span>
<span class="kd">var</span> <span class="nx">filename</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="nx">filename</span> <span class="o">=</span> <span class="nx">filename</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">filename</span><span class="p">.</span><span class="nx">lastIndexOf</span><span class="p">(</span><span class="dl">"</span><span class="s2">.</span><span class="dl">"</span><span class="p">));</span> <span class="c1">//just add this line to the construction.      </span>
 
<span class="c1">// execute for each group</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">y</span> <span class="o">&amp;</span><span class="nx">lt</span><span class="p">;</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">currentGroup</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">[</span><span class="nx">y</span><span class="p">];</span>
 
    <span class="c1">// check layer is visible</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">currentGroup</span><span class="p">.</span><span class="nx">kind</span> <span class="o">!=</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
 
    <span class="c1">// Pass sub layers to core script</span>
    <span class="kd">var</span> <span class="nx">groupLayers</span> <span class="o">=</span> <span class="nx">currentGroup</span><span class="p">.</span><span class="nx">layers</span><span class="p">;</span>
    <span class="c1">// hide others groups</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">t</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">t</span> <span class="o">&amp;</span><span class="nx">lt</span><span class="p">;</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">t</span><span class="o">++</span><span class="p">)</span>
        <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">[</span><span class="nx">t</span><span class="p">].</span><span class="nx">visible</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
    <span class="nx">currentGroup</span><span class="p">.</span><span class="nx">visible</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
 
    <span class="c1">// execute for each languages</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">t</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">t</span> <span class="o">&amp;</span><span class="nx">lt</span><span class="p">;</span> <span class="nx">langs</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">t</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">currentLang</span> <span class="o">=</span> <span class="nx">langs</span><span class="p">[</span><span class="nx">t</span><span class="p">];</span>
        <span class="kd">var</span> <span class="nx">fileContent</span><span class="p">;</span>
 
        <span class="c1">// Use absolute path for the JSON file.</span>
        <span class="kd">var</span> <span class="nx">langFilePath</span> <span class="o">=</span> <span class="nx">inputPath</span> <span class="o">+</span> <span class="o">&amp;</span><span class="nx">quot</span><span class="p">;</span><span class="sr">/&amp;quot; + filename + &amp;quot;-&amp;quot; + currentLang + &amp;quot;.json&amp;quot;</span><span class="err">;
</span>        <span class="kd">var</span> <span class="nx">fileToRead</span> <span class="o">=</span> <span class="nx">File</span><span class="p">(</span><span class="nx">langFilePath</span><span class="p">);</span>
        <span class="nx">fileContent</span> <span class="o">=</span> <span class="nx">FILE_toString</span><span class="p">(</span><span class="nx">fileToRead</span><span class="p">);</span>
 
        <span class="c1">// Use default language if file not found!</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">fileContent</span> <span class="o">===</span> <span class="o">&amp;</span><span class="nx">quot</span><span class="p">;</span><span class="o">&amp;</span><span class="nx">quot</span><span class="p">;)</span> <span class="p">{</span>
            <span class="kd">var</span> <span class="nx">langFilePath2</span> <span class="o">=</span> <span class="nx">inputPath</span> <span class="o">+</span> <span class="o">&amp;</span><span class="nx">quot</span><span class="p">;</span><span class="sr">/&amp;quot; + filename + &amp;quot;-&amp;quot; + langFallback + &amp;quot;.json&amp;quot;</span><span class="err">;
</span>            <span class="kd">var</span> <span class="nx">fileToRead2</span> <span class="o">=</span> <span class="nx">File</span><span class="p">(</span><span class="nx">langFilePath2</span><span class="p">);</span>
            <span class="nx">fileContent</span> <span class="o">=</span> <span class="nx">FILE_toString</span><span class="p">(</span><span class="nx">fileToRead2</span><span class="p">);</span>
        <span class="p">}</span>
 
        <span class="kd">var</span> <span class="nx">messages</span> <span class="o">=</span> <span class="nx">JSON_parse</span><span class="p">(</span><span class="nx">fileContent</span><span class="p">);</span>
 
        <span class="c1">// Translate each text layers and sub layers</span>
        <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span><span class="nx">span</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">mce_SELREST_start</span><span class="dl">"</span> <span class="nx">style</span><span class="o">=</span><span class="dl">"</span><span class="s2">overflow:hidden;line-height:0;</span><span class="dl">"</span><span class="o">&gt;&amp;</span><span class="err">#</span><span class="mi">65279</span><span class="p">;</span><span class="o">&lt;</span><span class="sr">/span&gt;&amp;lt; groupLayers.length; i++</span><span class="se">)</span><span class="sr"> </span><span class="err">{
</span>            <span class="kd">var</span> <span class="nx">currLayer</span> <span class="o">=</span> <span class="nx">groupLayers</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
 
            <span class="c1">// check layer is visible</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">currLayer</span><span class="p">.</span><span class="nx">visible</span> <span class="o">===</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
 
            <span class="c1">// check layer is text</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">currLayer</span><span class="p">.</span><span class="nx">kind</span> <span class="o">!=</span> <span class="nx">LayerKind</span><span class="p">.</span><span class="nx">TEXT</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
 
            <span class="c1">// get translation</span>
            <span class="kd">var</span> <span class="nx">translation</span> <span class="o">=</span> <span class="nx">messages</span><span class="p">[</span><span class="nx">currLayer</span><span class="p">.</span><span class="nx">name</span><span class="p">];</span>
 
            <span class="c1">// check translation founded</span>
            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">translation</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
 
            <span class="c1">// set new text</span>
            <span class="nx">currLayer</span><span class="p">.</span><span class="nx">textItem</span><span class="p">.</span><span class="nx">contents</span> <span class="o">=</span> <span class="nx">translation</span><span class="p">;</span>
        <span class="p">}</span>
 
        <span class="c1">// save</span>
        <span class="c1">// TODO: create outputPath</span>
        <span class="kd">var</span> <span class="nx">file</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">File</span><span class="p">(</span><span class="nx">outputPath</span> <span class="o">+</span> <span class="o">&amp;</span><span class="nx">quot</span><span class="p">;</span><span class="sr">/&amp;quot; + filename + &amp;quot;-&amp;quot; + currentLang + &amp;quot;-&amp;quot; + </span><span class="se">(</span><span class="sr">y + 1</span><span class="se">)</span><span class="sr"> + &amp;quot;.png&amp;quot;</span><span class="se">)</span><span class="err">;
</span>        <span class="kd">var</span> <span class="nx">saveOptions</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PNGSaveOptions</span><span class="p">();</span>
        <span class="nx">doc</span><span class="p">.</span><span class="nx">saveAs</span><span class="p">(</span><span class="nx">file</span><span class="p">,</span> <span class="nx">saveOptions</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">Extension</span><span class="p">.</span><span class="nx">LOWERCASE</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/upload/group-screenshot000.png" alt="group-ScreenShot000" /></p>

<p><img src="/upload/group-screenshot001.png" alt="group-ScreenShot001" /></p>

<p><img src="/upload/group-screenshot002-1.png" alt="group-ScreenShot002" /></p>

<p><img src="/upload/group-screenshot003.png" alt="group-ScreenShot003" /></p>

<p><img src="/upload/group-screenshot004.png" alt="group-ScreenShot004" /></p>

<p><img src="/upload/group-screenshot005.png" alt="group-ScreenShot005" /></p>

<p><img src="/upload/group-screenshot006.png" alt="group-ScreenShot006" /></p>

<p><img src="/upload/group-screenshot007.png" alt="group-ScreenShot007" /></p>

<h2 id="example--external-project">Example – External Project</h2>

<p>text-group-translate.bat</p>

<pre class="language-javascript"><span class="token string">"C:\Program Files (x86)\Photoshop\Photoshop.exe"</span> c<span class="token punctuation">:</span>\Users\damien\git\photoshop<span class="token operator">-</span>script<span class="token operator">-</span>text<span class="token operator">-</span>translator\text<span class="token operator">-</span>groups<span class="token operator">-</span>translate<span class="token punctuation">.</span>jsx
</pre>

<p><img src="/upload/example-screenshot002.png" alt="post-image" /></p>

<p><img src="/upload/example-screenshot004.png" alt="post-image" /></p>

<p><img src="/upload/example-screenshot005-1.png" alt="post-image" /></p>

<p><img src="/upload/example-screenshot006.png" alt="post-image" /></p>

<p><img src="/upload/example-screenshot007.png" alt="post-image" /></p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://github.com/fabianmoronzirfas/extendscript/wiki/Read-In-JSON-From-File-And-DONT-Eval">Photoshop read json file</a></li>
  <li><a href="http://creativetuts.com/photoshop-script-determine-layer-kind/">Photoshop get layer type</a></li>
</ul>

<p>Source</p>

<p><a href="https://github.com/DamienFremont/damienfremont.com-blog-labs/tree/master/20190620-photoshop-script-translate-text-then-export">https://github.com/DamienFremont/damienfremont.com-blog-labs/tree/master/20190620-photoshop-script-translate-text-then-export</a></p>]]></content><author><name>Damien Fremont</name></author><category term="assets" /><category term="javascript" /><category term="photoshop" /><category term="tools" /><summary type="html"><![CDATA[Photoshop script to translate text layers based on language JSON files, then export project to PNG images for each language...]]></summary></entry><entry><title type="html">How to Photoshop Script: Export Layers to PNG Files</title><link href="http://damienfremont.com/2019/05/25/photoshop-script-export-layers-to-png" rel="alternate" type="text/html" title="How to Photoshop Script: Export Layers to PNG Files" /><published>2019-05-25T00:00:00+00:00</published><updated>2019-05-25T00:00:00+00:00</updated><id>http://damienfremont.com/2019/05/25/photoshop-script-export-layers-to-png</id><content type="html" xml:base="http://damienfremont.com/2019/05/25/photoshop-script-export-layers-to-png"><![CDATA[<p><img src="/upload/screenshot006.png" alt="post-image" /></p>

<p>When you don’t want to do repetitive tasks in Photoshop… This script exports root layers from your current project to separate PNG files.</p>

<p>Usage: exporting sketchs to character animation, mock-up, code, etc.</p>

<h2 id="scripts">Scripts</h2>

<p>layers-exporter.bat</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"C:</span><span class="se">\P</span><span class="s2">rogram Files (x86)</span><span class="se">\P</span><span class="s2">hotoshop</span><span class="se">\P</span><span class="s2">hotoshop.exe"</span> %cd%<span class="se">\l</span>ayers-exporter.jsx<span class="sb">`</span>
</code></pre></div></div>

<p>layers-exporter.jsx</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">output</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">getenv</span><span class="p">(</span><span class="dl">"</span><span class="s2">USERPROFILE</span><span class="dl">"</span><span class="p">)</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">/Documents/</span><span class="dl">"</span><span class="p">;</span>
 
<span class="c1">//Get the currently opened Photoshop document</span>
<span class="kd">var</span> <span class="nx">doc</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">activeDocument</span><span class="p">;</span>
 
<span class="c1">//Show each layer each time and save a snapshot</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
 
  <span class="c1">//Hide all the layers</span>
  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&lt;</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">[</span><span class="nx">j</span><span class="p">].</span><span class="nx">visible</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
  <span class="p">}</span>
 
  <span class="c1">// show layer</span>
  <span class="kd">var</span> <span class="nx">layerIndex</span> <span class="o">=</span> <span class="nx">i</span><span class="p">;</span>
  <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">[</span><span class="nx">layerIndex</span><span class="p">].</span><span class="nx">visible</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

  <span class="kd">var</span> <span class="nx">layerName</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">layers</span><span class="p">[</span><span class="nx">layerIndex</span><span class="p">].</span><span class="nx">name</span><span class="p">;</span>
  <span class="kd">var</span> <span class="nx">filename</span> <span class="o">=</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
  <span class="nx">filename</span> <span class="o">=</span> <span class="nx">filename</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">filename</span><span class="p">.</span><span class="nx">lastIndexOf</span><span class="p">(</span><span class="dl">"</span><span class="s2">.</span><span class="dl">"</span><span class="p">));</span> <span class="c1">//just add this line to the construction.</span>
 
  <span class="c1">// save</span>
  <span class="kd">var</span> <span class="nx">file</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">File</span><span class="p">(</span><span class="nx">output</span> <span class="o">+</span> <span class="nx">filename</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">_</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">layerName</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">.png</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">var</span> <span class="nx">saveOptions</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PNGSaveOptions</span><span class="p">();</span>
  <span class="nx">doc</span><span class="p">.</span><span class="nx">saveAs</span><span class="p">(</span><span class="nx">file</span><span class="p">,</span> <span class="nx">saveOptions</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">Extension</span><span class="p">.</span><span class="nx">LOWERCASE</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="usage">Usage</h2>

<ol>
  <li>save these two scipts at the same location on your machine</li>
  <li>Open a photoshop file with layers</li>
  <li>Execute layers-exporter.bat, from command line or by clicking on it</li>
  <li>Get your results from your Documents folder</li>
</ol>

<p>NOTE: edit output value in layers-exporter.jsx if you want to change the target folder</p>

<p>NOTE: only ‘root’ layers or groups will generate files, you don’t have to worry about a million files from your project tree.</p>

<h2 id="example">Example</h2>

<p><img src="/upload/screenshot000.png" alt="post-image" /></p>

<p><img src="/upload/screenshot002.png" alt="post-image" /></p>

<p><img src="/upload/screenshot003.png" alt="post-image" /></p>

<p><img src="/upload/screenshot005.png" alt="post-image" /></p>

<p><img src="/upload/screenshot006.png" alt="post-image" /></p>

<p>Source</p>

<p><a href="https://github.com/DamienFremont/damienfremont.com-blog-labs/tree/master/20190525-photoshop-script-export-layers-to-png">https://github.com/DamienFremont/damienfremont.com-blog-labs/tree/master/20190525-photoshop-script-export-layers-to-png</a></p>]]></content><author><name>Damien Fremont</name></author><category term="assets" /><category term="javascript" /><category term="photoshop" /><category term="tools" /><summary type="html"><![CDATA[When you don’t want to do repetitive tasks in Photoshop… This script exports root layers from your current project...]]></summary></entry><entry><title type="html">Unity3d Reducing Apk Size For Mobile</title><link href="http://damienfremont.com/2019/05/04/unity3d-reducing-apk-size-for-mobile" rel="alternate" type="text/html" title="Unity3d Reducing Apk Size For Mobile" /><published>2019-05-04T00:00:00+00:00</published><updated>2019-05-04T00:00:00+00:00</updated><id>http://damienfremont.com/2019/05/04/unity3d-reducing-apk-size-for-mobile</id><content type="html" xml:base="http://damienfremont.com/2019/05/04/unity3d-reducing-apk-size-for-mobile"><![CDATA[<p><img src="/upload/diapositive1_002.png" alt="post-image" /></p>

<p>Keeping the file size of the built app to a minimum is important, especially for mobile devices or for app stores that impose a size limit.</p>

<p>Note: this article was made using Unity 2019.1</p>

<p><strong>Edit (2020):</strong> use App Bundle now. Your app will be splitted in small parts for download, it’s one package for all hardware plateformes …and Google will force you into it next year anyway.</p>

<h2 id="content">Content</h2>

<ol>
  <li>Don’t expect too much from Unity</li>
  <li>Remove useless assets, models, script etc</li>
  <li>Remove the parts of the engine you aren’t using</li>
  <li>Use small size textures/images and compression</li>
  <li>If you have audio, Convert songs to ogg</li>
  <li>Enable IL2CPP native compilation</li>
  <li>Build two separate APK</li>
</ol>

<h2 id="1-dont-expect-too-much-from-unity">1. Don’t expect too much from Unity</h2>

<p>Half of that size is taken up by the Unity engine. You won’t be able to get it much smaller and later version of Unity are bigger !!!</p>

<p>But no matter what you do, I doubt you’ll get a completed game smaller than ~20mb. It’s just a downside of using a game engine that has lots of features you don’t use.</p>

<p>Example: in 7-Zip &gt; Open your APK file, wait, sort by Size …’lib’ use 10 mo, ‘assets\bin\Data\Managed’ 6 mo (compressed) …so that’s 16 mo for the engine in your APK, (uncompressed to compressed size is not linear, bad with lib, good with assets)</p>

<p><img src="/upload/screen-shot-05-04-19-at-01_003.png" alt="post-image" /></p>

<h2 id="2-remove-useless-assets-models-script-etc">2. Remove useless assets, models, script etc</h2>

<p>It’s common sense, but it’s easy to forget some large picture or sound files in your project.</p>

<p>Remove any stuff you don’t need, such as scenes that are in your build but not used.</p>

<p>Example: in WinDirStat &gt; menu:File &gt; Open Folder &gt; choose your unity3d project &gt; Ok, Wait, Navigate to Assets folder, Check your files …maybe there are some you don’t use or need.</p>

<p><img src="/upload/screen-shot-05-04-19-at-12_002.png" alt="post-image" /></p>

<p>Example: in Unity3D &gt; menu: Window &gt; General &gt; Console), click the small drop-down panel in the top right, and select Open Editor Log …you’ll see only your files in uncompressed size, so it won’t be true in your APK !!! (uncompressed to compressed size is not linear, bad with DLL, good with picture file)</p>

<p><img src="/upload/screen-shot-05-04-19-at-01.png" alt="post-image" /></p>

<h2 id="3-remove-the-parts-of-the-engine-you-arent-using">3. Remove the parts of the engine you aren’t using</h2>

<p>Remove the parts of the engine you arent using instead of including all of it. Like Physics, x86 Architecture, .NET 4.x.</p>

<p>Unity supports two .NET API compatibility levels.: .NET 4.x and .NET Standard 2.0. The .NET Standard 2.0 restricts you to a smaller subset of the .NET API, which can help keep size down.</p>

<p>Change the build to just ARM you don’t need x86. Also check out the stripping level.</p>

<p>Example: in Unity3D &gt; Menu &gt; Build Settings &gt; (new window) &gt; Player Settings &gt; (new window) &gt; Player &gt; Settings &gt; ARMv7:true, x86:false, API Compatibility Level: NET Standard 2.0</p>

<p><img src="/upload/screen-shot-05-04-19-at-01_002.png" alt="post-image" /></p>

<h2 id="4-use-small-size-texturesimages-and-compression">4. Use small size textures/images and compression</h2>

<p>Try reducing the sizes of your assets. Use compression on images (and reduce their resolution), reduce the bitrate of any audio, especially music.</p>

<p>You can reduce the colour depth of images that don’t need transparency.</p>

<p>Example: in Photoshop &gt; Menu &gt; File &gt; Save for web &gt; PNG 8, Image Size …File size is reduce from 600 ko to 40 ko !!!</p>

<p><img src="/upload/screen-shot-05-04-19-at-12.png" alt="post-image" /></p>

<h2 id="5-if-you-have-audio-convert-songs-to-ogg">5. If you have audio, Convert songs to ogg</h2>

<p>Use a tool to convert sound files to .ogg format.</p>

<p>Example: <a href="http://www.the-converter.net/fr/audio">http://www.the-converter.net/fr/audio</a> &gt; ogg, 16 KHz, 32 kb/s, mono</p>

<p><img src="/upload/screen-shot-05-04-19-at-03.png" alt="post-image" /></p>

<h2 id="6-enable-il2cpp-native-compilation">6. Enable IL2CPP native compilation</h2>

<p>Install NDK and choose IL2CPP</p>

<p>Example: in Unity3D &gt; Menu &gt; Build Settings &gt; (new window) &gt; Player Settings &gt; (new window) &gt; Player &gt; Settings &gt; ‘Scripting Backend’:’IL2CPP’</p>

<p><img src="/upload/screen-shot-05-04-19-at-04.png" alt="post-image" /></p>

<h2 id="7-build-two-separate-apk">7. Build two separate APK</h2>

<p>Googleplay will impose ARM64. In the meantime, you’ll need to build for ARMv7 and ARM64. It’s only possible with IL2CPP activated.</p>

<p>But ARM64, like x86, adds about 5 MO to your APK file.</p>

<p>Example: in Unity3D &gt; Menu &gt; Build Settings &gt; (new window) &gt; Player Settings &gt; (new window) &gt; Player &gt; Settings &gt; ‘Split APK by Archi’:true</p>

<p><img src="/upload/screen-shot-05-04-19-at-04_002.png" alt="post-image" /></p>

<h2 id="resources">Resources</h2>

<p><a href="https://docs.unity3d.com/Manual/ReducingFilesize.html">Reducing the file size of your build</a></p>

<p><a href="https://forum.unity.com/threads/how-to-reduce-apk-size.493746/">How to reduce apk size?</a></p>

<p><a href="https://stackoverflow.com/questions/28100362/how-to-reduce-the-size-of-an-apk-file-in-unity">How to reduce the size of an APK file in Unity?</a></p>

<p><a href="https://www.reddit.com/r/Unity2D/comments/6jf887/how_can_we_reduce_apk_file_size_in_unity_i_made_a/">How can we reduce apk file size in unity i made a game like flappy bird it is over 32 mb how to reduce the size .</a></p>

<p><a href="https://divillysausages.com/2016/01/21/performance-tips-for-unity-2d-mobile/">Performance tips for Unity 2d mobile</a></p>]]></content><author><name>Damien Fremont</name></author><category term="gamedev" /><category term="android" /><category term="unity3d" /><summary type="html"><![CDATA[Keeping the file size of the built app to a minimum is important, especially for mobile devices or...]]></summary></entry><entry><title type="html">Gamedev Trello For Project Management</title><link href="http://damienfremont.com/2018/11/24/gamedev-trello-for-project-management" rel="alternate" type="text/html" title="Gamedev Trello For Project Management" /><published>2018-11-24T00:00:00+00:00</published><updated>2018-11-24T00:00:00+00:00</updated><id>http://damienfremont.com/2018/11/24/gamedev-trello-for-project-management</id><content type="html" xml:base="http://damienfremont.com/2018/11/24/gamedev-trello-for-project-management"><![CDATA[<p><img src="/upload/screen-shot-11-24-18-at-07-52-pm1.png" alt="Screen Shot 11-24-18 at 07.52 PM.PNG" /></p>

<p>Do you find project management stressful? Do you struggle to keep your tasks, team members, and goals organized? Here’s how to manage your project with a simple and free tool like Trello. Whether on desktop or mobile.</p>

<p>Staying on top of these things can be frustrating—but with the right tool, the entire process can become streamlined, efficient, and easier for everyone. The right tool will turn you into a project management master, able to get updates, reprioritize tasks, and communicate with your team on the fly.</p>

<p>What’s the tool that will give you these project management superpowers? Trello.</p>

<p>There are a lot of project management tools out there. But Trello stands out from the crowd with its simplicity. It provides a straightforward system for organizing and monitoring projects at a glance. It’s so simple, in fact, that you might be tempted to pass it up in favor of something that requires a week-long training course.</p>

<p>But Trello’s simplicity is its greatest strength. Let’s take a look at how it can turn you into an organizational ace.</p>

<h2 id="contents">Contents</h2>

<ul>
  <li>Create a Trello account</li>
  <li>Create a Trello Team</li>
  <li>Create a Trello Board
    <ul>
      <li>Lists: categorizing your activities</li>
      <li>Labels: categorizing your cards</li>
    </ul>
  </li>
  <li>Add some tasks to Board
    <ul>
      <li>Flow example</li>
    </ul>
  </li>
  <li>Install the mobile app
    <ul>
      <li>Share it with team members</li>
    </ul>
  </li>
</ul>

<p>The final board:</p>

<p><img src="/upload/screen-shot-11-24-18-at-07-52-pm1.png" alt="Screen Shot 11-24-18 at 07.52 PM.PNG" /></p>

<p><img src="/upload/kanban.png" alt="kanban" /></p>

<hr />

<h2 id="create-a-trello-account">Create a Trello account</h2>

<p>Go to Trello’s signup and use the ‘Sign up with Google’.</p>

<p><a href="https://trello.com/signup">https://trello.com/signup</a></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-17-pm.png?w=361" alt="Screen Shot 11-24-18 at 06.17 PM.PNG" /></p>

<hr />

<h2 id="create-a-trello-team">Create a Trello Team</h2>

<p>Each one can be used by a whole company, a separate team within it, a non-work related group such as your family or friends, or just yourself. A Team unit can even represent a single complex project that has numerous sub-units.</p>

<p>Create a new team by clicking the “+” button at the left hand side of your Trello header. Add a name, picture and people to your team, then save</p>

<p><img src="/upload/file-yqish32629.png?w=271&amp;h=268" alt="file-YqisH32629.png" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-15-pm.png?w=409&amp;h=415" alt="Screen Shot 11-24-18 at 06.15 PM.PNG" /></p>

<hr />

<h2 id="create-a-trello-board">Create a Trello Board</h2>

<p>Within the Teams you create or join, the Board is the main working unit. It looks and feels like a whiteboard where you manage your work with sticky notes. Write new ones, move them around, and archive old ones.</p>

<p>Boards can be used in myriad ways. They can be dedicated to the <strong>whole workflow</strong> of a particular team or <strong>separate activities</strong> within it. Alternatively, they can focus on a <strong>single project or event</strong>.</p>

<p>From the team’s Boards tab, click “Create new board” in the Trello header directly to the left of your name, and select “Create Board.”</p>

<ol>
  <li>Set a name for your board, choose a background and a Team.</li>
  <li>Create your first list ‘About this board’</li>
  <li>Create your first card in it ‘What can you expect from this board?’</li>
  <li>Add a picture to the card with ‘attachment’, then complete decription</li>
  <li>And some other cards like ‘Release Target: 2018’ and ‘Tips for using this Board’</li>
</ol>

<p><img src="/upload/screen-shot-11-24-18-at-06-31-pm.png?w=284" alt="Screen Shot 11-24-18 at 06.31 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-34-pm.png?w=487" alt="Screen Shot 11-24-18 at 06.34 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-37-pm.png?w=487" alt="Screen Shot 11-24-18 at 06.37 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-36-pm.png?w=490" alt="Screen Shot 11-24-18 at 06.36 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-39-pm-001.png?w=485&amp;h=279" alt="Screen Shot 11-24-18 at 06.39 PM 001" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-38-pm.png?w=483&amp;h=278" alt="Screen Shot 11-24-18 at 06.38 PM" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-39-pm.png?w=485&amp;h=279" alt="Screen Shot 11-24-18 at 06.39 PM" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-28-pm.png?w=486" alt="Screen Shot 11-24-18 at 06.28 PM.PNG" /></p>

<hr />

<h3 id="lists-categorizing-your-activities">Lists: categorizing your activities</h3>

<p>The next thing in the Trello workflow is Lists. They’re the classifiers of your activities within a Board.</p>

<p>There are numerous ways to go about using Lists. If you stick to the classical Kanban approach of Trello, your lists would hold your to-dos, in-progress tasks, and things you’ve finished. Each list can represent any way of grouping tasks that makes sense for your work. This can be their subject area, priority level, responsible person, or other classifying criteria.</p>

<p>Use the following:</p>

<ol>
  <li>Idea</li>
  <li>Set for Development</li>
  <li>In Progress</li>
  <li>Done</li>
</ol>

<p><strong>Tips</strong></p>

<p>later you can add more lists,  for each release: coping Done’s content to Shipped for v1, Shipped for v2, etc</p>

<p><img src="/upload/kanban.png?w=500&amp;h=250" alt="kanban.png" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-06-44-pm.png?w=505" alt="Screen Shot 11-24-18 at 06.44 PM.PNG" /></p>

<hr />

<h3 id="labels-categorizing-your-cards">Labels: categorizing your cards</h3>

<p>Trello labels can be used for tracking which types activities your team is spending time on.</p>

<p>To set labels in Trello, Just open the menu and click on “Labels”. There you can give names to the default labels or create new ones – as many as you like!</p>

<p>Use the following:</p>

<ol>
  <li>Need attention (something need to be rework before work)</li>
  <li>Must have (core elements to your project, like gameplay)</li>
  <li>Nice to have (optionnal elements to your project, like lootbox, skins)</li>
  <li>Whishlist proposal (An idea that you maybe want in the Set for development list)</li>
  <li>Hot! (it’s a bug, an issue or it’s urgent)</li>
</ol>

<p><strong>Tips</strong></p>

<p>You can also add and remove labels pressing “l” to bring up a pop-over menu of labels. Toggle an individual label by pressing numbers between 1 and 9. Individual label shortcuts are mapped below.</p>

<p>You can use ASCII art to make icons for your labels:  /!\ [x] [_] &lt;?&gt; ♥ …</p>

<h3><img src="/upload/screen-shot-11-24-18-at-06-45-pm.png?w=300" alt="Screen Shot 11-24-18 at 06.45 PM.PNG" /></h3>

<p><img src="/upload/screen-shot-11-24-18-at-08-00-pm.png" alt="Screen Shot 11-24-18 at 08.00 PM.PNG" /></p>

<hr />

<h2 id="add-some-tasks-to-board">Add some tasks to Board</h2>

<h3 id="cards-where-work-gets-done">Cards: where work gets done</h3>

<p>The smallest unit of work in Trello is the card. Cards are super flexible and allow you to include <strong>descriptions</strong>, <strong>attachments</strong>, <strong>subtasks or</strong> <strong>checklists</strong>, <strong>due</strong> <strong>dates</strong> <strong>and times</strong>, <strong>assignees</strong>, and <strong>labels</strong>. A card typically represents a single task in your workflow.</p>

<p>You can <strong>upload images and files</strong> from your device, or from Google Drive, Dropbox, Box, or OneDrive. And with a simple click-and-drag, cards can be moved from one list to another.</p>

<ul>
  <li>Some cards are simple: use a name and description</li>
  <li>Some cards are heavy: use a checklist</li>
  <li>Some tasks have deadline: use a due date</li>
  <li>then put a label if a type is needed</li>
</ul>

<p><strong>Tips</strong></p>

<p>You can add multiple card in a one-shot action if the text in it have multiple lines.</p>

<p><img src="/upload/screen-shot-11-24-18-at-07-04-pm.png?w=505&amp;h=286" alt="Screen Shot 11-24-18 at 07.04 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-07-14-pm.png?w=507&amp;h=511" alt="Screen Shot 11-24-18 at 07.14 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-07-18-pm.png?w=511&amp;h=347" alt="Screen Shot 11-24-18 at 07.18 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-07-19-pm.png?w=521&amp;h=354" alt="Screen Shot 11-24-18 at 07.19 PM.PNG" /></p>

<hr />

<h2 id="flow-example">Flow example</h2>

<p>Start</p>

<ol>
  <li>Create basic tasks for your project (tools install, helloworld project, brainstorming)</li>
  <li>Do it!</li>
  <li>Only when done, create a lot of tasks (idea1, idea 2, idea 3)</li>
  <li>Priorize it, add a ‘whishlist’ label if needed</li>
</ol>

<p>Iterate</p>

<ol>
  <li>Move 3-5 of your top ‘idea’ cards to ‘set for dev’</li>
  <li>Do it! Experiment with it !</li>
  <li>When Done, repeat</li>
</ol>

<p>End</p>

<ol>
  <li>When you hit your target for v1, copy it to a new list ‘shipped for v1’ for archive purpose (Done is now empty)</li>
  <li>Release your game</li>
  <li>Then restart a new version</li>
</ol>

<p><strong>Tips</strong></p>

<p>Always finish a card before working on the next.</p>

<p>Always start your card with a verb if it’s a task.</p>

<p>Check checklists’ boxes to follow Card advancement.</p>

<p><img src="/upload/screen-shot-11-24-18-at-07-11-pm.png" alt="Screen Shot 11-24-18 at 07.11 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-07-22-pm.png" alt="Screen Shot 11-24-18 at 07.22 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-07-39-pm.png" alt="Screen Shot 11-24-18 at 07.39 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-07-41-pm.png" alt="Screen Shot 11-24-18 at 07.41 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-07-42-pm.png" alt="Screen Shot 11-24-18 at 07.42 PM.PNG" /></p>

<p><img src="/upload/screen-shot-11-24-18-at-07-45-pm.png" alt="Screen Shot 11-24-18 at 07.45 PM.PNG" /></p>

<hr />

<h2 id="install-the-mobile-app">Install the mobile app</h2>

<p>The app is available on desktop web browser or on your mobile app : use it at anytime whenever you have an idea (transport, bed, work, etc).</p>

<p><a href="https://trello.com/platforms">https://trello.com/platforms</a></p>

<p><img src="/upload/proxy-duckduckgo-com.png?w=493" alt="proxy.duckduckgo.com.png" /></p>

<hr />

<h2 id="share-it-with-team-members">Share it with team members</h2>

<p>You can invite others to your boards and teams by sharing a special link with them.</p>

<p><img src="/upload/file-0p7vksyinx.gif" alt="file-0p7VKSYInx.gif" /></p>

<p><img src="/upload/file-pryohijndp.gif" alt="file-PryOhIJNDP.gif" /></p>

<hr />

<h2 id="references">References</h2>

<p><a href="https://trello.com/b/TTAVI7Ny/ue4-roadmap">https://trello.com/b/TTAVI7Ny/ue4-roadmap</a></p>

<p><a href="https://blog.trello.com/how-to-scrum-and-trello-for-teams-at-work">https://blog.trello.com/how-to-scrum-and-trello-for-teams-at-work</a></p>

<p><a href="https://help.trello.com/article/705-creating-a-new-team">https://help.trello.com/article/705-creating-a-new-team</a></p>

<p><a href="https://help.trello.com/article/717-adding-people-to-a-board">https://help.trello.com/article/717-adding-people-to-a-board</a></p>]]></content><author><name>Damien Fremont</name></author><category term="gamedev" /><category term="agile" /><category term="tools" /><summary type="html"><![CDATA[Do you find project management stressful? Do you struggle to keep your tasks, team members, ...]]></summary></entry><entry><title type="html">Devfest Nantes 2018 Retours</title><link href="http://damienfremont.com/2018/10/22/devfest-nantes-2018-retours" rel="alternate" type="text/html" title="Devfest Nantes 2018 Retours" /><published>2018-10-22T00:00:00+00:00</published><updated>2018-10-22T00:00:00+00:00</updated><id>http://damienfremont.com/2018/10/22/devfest-nantes-2018-retours</id><content type="html" xml:base="http://damienfremont.com/2018/10/22/devfest-nantes-2018-retours"><![CDATA[<p><img src="/upload/devfestnantes2018-log.pict-site.png" alt="picture" /></p>

<p>Nous avons pu participer pour la première fois à la conférence DevFest à Nantes le 18 octobre 2018. Dans cet article à deux mains, nous vous proposons quelques morceaux choisis sur cette expérience et ses talks/conférences.</p>

<p>Pour rappel, DevFest est un événement présent dans plusieurs pays et villes, et très orienté technique.</p>

<ul>
  <li><a href="https://devfest.gdgnantes.com/">-&gt; Le site</a></li>
</ul>

<h1 id="jeudi-18-octobre---1er-jour">JEUDI 18 OCTOBRE - 1ER JOUR</h1>

<p>Les moments forts: la keynote, les talks, les codelabs et quelques speakers de renom (ex: les GDE ‘Google Developer Expert’, David Gageot, etc).</p>

<hr />

<h2 id="8h30---accueil">8h30 - Accueil</h2>

<p>Du monde! Du monde! Content d’être venu tôt pour être bien placé durant la conférence.</p>

<p>Bon point pour l’organisation: le badge est aussi un planning des talks ET un plan du site. C’est beaucoup plus pratique que d’autres conférences (le palais des congrès c’est grands! et il n’y a plus besoin d’application mobile, car vous avez l’essentiel autour du cou).</p>

<ul>
  <li><a href="https://twitter.com/devfestnantes/status/1052838192649367552">-&gt; Aperçu des stands</a></li>
  <li><a href="https://mobile.twitter.com/mperrien44/status/1052808577570635776">-&gt; Sans oublier SQLI !!!</a></li>
</ul>

<p><img src="/upload/devfestnantes2018-log.pict-accueil.png" alt="picture" /></p>

<p><img src="/upload/devfestnantes2018-log.pict-sqli.png" alt="picture" /></p>

<h2 id="9h00---keynote-douverture">9h00 - Keynote d’ouverture</h2>

<p>Une grosse vidéo d’intro sur le thème de la conquête spatiale, suivi d’un talk.</p>

<p>Nous avons eu la chance d’avoir un speaker expérimenté pour ce premier talk: Christian HEILMANN (lead developer at Microsoft Germany). Rien de ‘disruptif’, mais quelques piqures de rappel: les développeurs doivent se comporter en professionnels (ex: attention à la ‘hype’, se faire à l’idée qu’une application est là pour 10 ans, la dette technique doit être gérée, et nous devons construire en partant de l’existant plutôt que de réinventer constamment la même chose).</p>

<ul>
  <li><a href="https://twitter.com/devfestnantes/status/1052821317303590912">-&gt; L’intro de la keynote</a></li>
  <li><a href="https://twitter.com/codepo8">-&gt; Le twitter du speaker</a></li>
</ul>

<p><img src="/upload/devfestnantes2018-log.pict-intro.png" alt="picture" /></p>

<p><img src="/upload/devfestnantes2018-log.pict-keynote.jpg" alt="picture" /></p>

<hr />

<p>Quelques ‘talks’ et ‘codelab’ pour la suite (présentation et travaux pratiques).</p>

<h2 id="11h00---the-next-big-thing-in-client-side-templating">11h00 - The next big thing in (client side) templating</h2>

<p>Un talk sur le futur des templating en web (faire des vues pour nos sites et applications).</p>

<p>Le speaker de chez Google a retracé l’histoire du templating à travers les âges du web:</p>
<ul>
  <li>‘concat’: Au début il y avait juste des chaines de caractères assemblées …pas très maintenable, mais simple.</li>
  <li>‘template’: ensuite il y eu le MVC, et donc son ‘V’ pour Vue séparée du code et sa traduction en template …un peu lourd, mais durable.</li>
  <li>‘react’: puis vint le JSX et de nouveau le mélange entre Vue et Code …beaucoup trop lié à la librairie de facebook, mais performant.</li>
</ul>

<p>Le futur semble aller vers le style de ReactJS, mais basé sur les standards du web, les WebComponents du W3C, et donc sur ce que propose le navigateur.</p>
<ul>
  <li>‘polymer’: librairie de Google qui permet dès aujourd’hui de coder comme pour demain. Pour résumer, en faisant du ReactJS, à la sauce ES6, donc du JavaScript, en utilisant les class ES6 et le templating ES6 à la place du JSX de ReactJS.</li>
</ul>

<p>Bilan? natif, performant, léger (&lt;1kb), futur-proof car fournit par le navigateur, meilleur pratiques de dev, pas de librairie tier.</p>

<ul>
  <li><a href="https://www.slideshare.net/FilipBruunBechLarsen/whats-next-in-clientside-templating-119875258">-&gt; La présentation sur le templating ES6</a></li>
  <li><a href="https://twitter.com/Filipbech">-&gt; Le twitter du speaker</a></li>
</ul>

<p><img src="/upload/devfestnantes2018-log.pict-jstemplating1.jpg" alt="picture" /></p>

<p><img src="/upload/devfestnantes2018-log.pict-jstemplating2.png" alt="picture" /></p>

<p><img src="/upload/devfestnantes2018-log.pict-jstemplating3.png" alt="picture" /></p>

<hr />

<p>Reprise à 14h après un bon repas!</p>

<h2 id="14h00---typographie-et-accessibilité">14h00 - Typographie et accessibilité</h2>

<p>Un talk à mi-chemin entre UX et Accessibilité, et un bon rappel sur la définition d’un handicap (comment rendre accessible son site ou applications, et pourquoi).</p>

<p>Rappel: Handicap ou limite ne correspondent pas forcément à une personne handicapée, au sens légal du terme. Beaucoup de troubles, d’invalidités ou de blessures temporaires entrent dans cette définition (rappel: 80% des handicaps ne se voient pas).</p>

<p>Derrière l’effort à fournir, en plus de la valeur légale, il y la valeur morale de pousser une solution UX vers RGAA.
Les enjeux: l’accessibilité numérique est un droit depuis quelques années. Il y a deux normes WCAG (W3C), RGAA (France) pour l’implémenter. Ces deux normes sont des méthodologies, non des outils. Elles ne fournissent pas de solutions, mais des critères pour vous aider à valider votre site. En France nous avons RGAA avec trois niveaux. Soit ‘A’: accessible, ‘AA’: confort (minimum légal), ‘AAA’ facultatif: handicap mental ou cognitif.</p>

<p>Concernant la typographie, il y a deux points d’attention:</p>
<ul>
  <li>VISU: visuelle, malvoyant, couleurs, contrastes</li>
  <li>DYS: dyslexie, lecture, gestes, chiffres, mémoire</li>
</ul>

<p>Les préconisations: reposer sur ce que fait la plateforme (navigateur) plutôt que votre code (JavaScript et widgets), afin de ne pas tout ‘casser’ quand l’utilisateur à un zoom, une navigation ou un écran différent. Utiliser la taille de police pour hiérarchiser le contenu. Toujours définir les tailles en unité ‘em’ (unité relative), que ce soit pour les polices, les interlignes ou les inter-paragraphes. La largeur de police en ‘ch’. Les polices ‘web-safe’ classiques (‘verdana’, ‘arial’, ‘tahoma’, ‘trebuchet’). Voir même laisser l’utilisateur personnaliser son expérience, via les polices, le contraste, le thème. Des outils automatisés existent pour détecter les erreurs flagrantes.</p>

<ul>
  <li><a href="https://devfest2018.access42.net/">-&gt; La présentation sur la Typographie et accessibilité</a></li>
  <li><a href="https://twitter.com/kReEsTaL">-&gt; Le twitter du speaker</a></li>
</ul>

<p><img src="/upload/devfestnantes2018-log.pict-typoaccess.jpg" alt="picture" /></p>

<p><img src="/upload/devfestnantes2018-log.pict-typoaccess2.png" alt="picture" /></p>

<h2 id="15h00---linfrastructure-as-code-avec-terraform">15h00 - L’infrastructure as code avec Terraform</h2>

<p>Un talk sur la nouvelle orientation de Terraform, qui passe de gestion d’infrastructure vers la gestion de ‘providers’ fournissant des APIs (Graphana, Cubernetis, base de données, outils), et donc une solution d’Infrastructure-As-Code.</p>

<p>Il propose une compatibilité accrue par rapport à ses concurrents: beaucoup de ‘providers’ pour Terraform sont maintenus par le produits tiers eux-mêmes (ex: produits Google). Et introduit son propre vocabulaire: topologie (‘topology’) des ressources (‘provider)’ qui ont des états (‘state’). En se basant autour d’un langage maison (malheureusement), le HCL, mais facile à lire et à générer par les outils (Ansible, utilise le YML, un langage standardisé lui).</p>

<p>Ses points forts:</p>
<ul>
  <li>provider-specific: Le code écrit est spécifique aux ressources. ex: un programme écrit pour un cloud Google ne fonctionnera pas sur un cloud Amazon.</li>
  <li>cloud-agnostic: Le workflow autour est générique. ex: la gestion de l’infra (‘topoly’) d’une infra sera la même que cela soit sur un cloud Amazon ou Google.</li>
</ul>

<p>TerraForm introduit de la gestion d’état dans Infrastructure-As-Code (contrairement à Ansible, qui ne gère que des script/code et des ressources). L’avantage est sur la modification de l’infrastructure. Par exemple: si une personne modifie manuellement une ressource (une VM), Terraform le détectera et jouera la partie de code concernant cette partie de l’infra, et seulement elle. Il profite également de modules via HTTP ou GitHub ou S3.</p>

<p>Conclusion: par rapport à Ansible, il y a plus de complexité introduite (via le code et la gestion d’états), mais maintenable et réactive (moins monolithique). Ansible est plus simple et rappel le python pour les Ops, mais il est difficile de rendre la solution modulaire. Terraform à essayer donc, mais quelques questions sur l’avenir du produit, notamment sa compatibilité à long terme avec les clouds et l’évolution de son langage.</p>

<ul>
  <li><a href="https://twitter.com/MathieuHerbert">-&gt; Le twitter du speaker</a></li>
</ul>

<p><img src="/upload/devfestnantes2018-log.pict-infraterraform.png" alt="picture" /></p>

<h2 id="16h00---a-la-découverte-des-algorithmes-génétiques">16h00 - A la découverte des algorithmes génétiques</h2>

<p>Un codelab cette fois (travaux pratiques sur deux heures) autour des algorithmes génétiques.</p>

<p>Une expérience un peu rude, car limitée dans le temps sur un sujet qui m’était inconnu: Il fallait résoudre un problème, très rapidement présenté, en étant par équipe, le tout en compétition, en seulement une heure, en installant son poste! Heureusement, après 20min d’égarement, nous avons compris quel était le sujet, et tout est rentré dans l’ordre.</p>

<p>Ce que j’en ai ressorti: quand il est question de machine-learning, nous y associons souvent les réseaux de neurones et des jeux de données massifs pour l’apprentissage. Ici le but est le même: la machine doit apprendre seule, mais plutôt que de renforcer des réseaux de neurones, nous allons soumettre des solutions aléatoires à la ‘sélection’ naturelle (comprendre: les moins bonnes solutions disparaissent), puis y ajouter une touche d’aléatoire (‘mutation’) pour éviter les impasses, jusqu’à obtenir un candidat adapté au problème (‘fit’).</p>

<p>En somme, du Darwinisme (algorithmes génétiques), plutôt que de l’apprentissage spécialisé (réseau de neurones).</p>

<p>Bilan: petit plaisir coupable, mon équipe (en violet ci-dessous) s’en est bien sortie, malgré un algo trop aléatoire …la nature fait bien les choses (le but était de produire une voiture qui roulait, testé dans un simulateur web).</p>

<ul>
  <li><a href="https://github.com/sebastienD/genetic-car/tree/master/genetic-car-starters/genetic-car-starter-java">-&gt; Le starter du codelab sur algorithmes génétiques</a></li>
  <li><a href="https://twitter.com/seb_express">-&gt; Le twitter du speaker</a></li>
  <li><a href="http://www.theprojectspot.com/tutorial-post/creating-a-genetic-algorithm-for-beginners/3">-&gt; Un peu de littérature pour comprendre</a></li>
</ul>

<p><img src="/upload/devfestnantes2018-log.pict-algogenetique1.png" alt="picture" /></p>

<p><img src="/upload/devfestnantes2018-log.pict-algogenetique2.png" alt="picture" /></p>

<hr />

<p>Et enfin quelques ‘quickies’ pour finir (talks courts et moins poussés).</p>

<h1 id="bilan">BILAN</h1>

<p>Une conférence sympa, du choix entre les sujets (web, IoT, mobile, etc), pour tous les niveaux et dans tous les formats (talk, codelab). Mais, des salles un peu surchargées et quelques soucis pour la retransmission des talks dans les salles annexes.</p>

<ul>
  <li><a href="https://twitter.com/devfestnantes">-&gt; Retrouver tout les moments forts sur le twitter du DevFest Nantes</a></li>
</ul>

<h1 id="presentation">PRESENTATION</h1>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Justine POEZEVARA

jpoezevara@sqli.com
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Damien FREMONT
Expert technique SQLI
En mission au Ministère des Affaires Etrangères, Nantes
https://twitter.com/Damien_Fremont
dfremont@sqli.com
</code></pre></div></div>]]></content><author><name>Damien Fremont</name></author><category term="other" /><summary type="html"><![CDATA[Nous avons pu participer pour la première fois à la conférence DevFest à Nantes le 18 octobre 2018...]]></summary></entry></feed>