Install PHP 7 from source on Raspbian/Debian

This tutorial cover Raspbian PHP 7 installation as well as Apache 2 configuration.

Prerequisite: Apache 2 installation exists

1. Download PHP source code from and then decompressed it to have a PHP folder contains source code

2. On the terminal, install the libxml2:
sudo apt-get install libxml2-dev

3. Inside the PHP folder at step #1,configure the build:
./configure ––with-apxs2=/usr/local/apache2/bin/apxs ––with-mysqli ––enable-mbstring

  • apxs was installed when installing Apache 2. In this example, the Apache 2 was installed at /usr/local/apache2.
  • The default location of php.ini is /usr/local/lib/. Use ––with-config-file-path=<path> if you need to put the php.ini file at somewhere else.
  • ––with-mysql was removed, use ––with-mysqli instead (
  • ––enable-mbstring is required for using phpMyAdmin

4. Install:
sudo make
sudo make install

5. Verify:
php -v

Here are a few more steps to configure Apache 2 to support PHP:

1. Open the httpd.conf to edit:
sudo nano /usr/local/apache2/conf/httpd.conf

2. Make sure the following line exists and not commented:
LoadModule php7_module modules/

3. Add the following lines to let Apache parse PHP files:
<FilesMatch “\.phps$”>
        SetHandler application/x-httpd-php-source

4. Enable mod_rewrite:
LoadModule rewrite_module modules/
RewriteEngine On

5. Save and close Nano by pressing Ctrl+X and Y

6. Restart Apache 2:
sudo /usr/local/apache2/bin/apachectl restart

Also, check out Install MariaDB / MySQL on Raspbian / Debian

Install Apache 2 web server from source on Raspbian

Image credit:

This guideline is for working on Raspbian – a Linux distro for Raspberry Pi, but other Linux operating systems have similar steps.

  1. Download source from It should be in a compressed file, e.g.: httpd-2.4.28.tar.bz2. After decompressing it, we have a httpd-<version> folder.
  2. Install PCRE library from the terminal:
    sudo apt-get install libpcre3-dev
  3. Download source of APR and APR-util from They’re all in compressed files, e.g.:  apr-1.6.2.tar.gzapr-util-1.6.0.tar.gz. After decompressing them, we have the following folders: apr-<version>, apr-util-<version>. Rename them accordingly to apr and apr-util and then move them into the folder at step #1 to this path: httpd-<version>/srclib/.
    The folder structure should be like this:
  4. Open the terminal and issue the following commands in the httpd-<version> folder to build and install Apache 2:
    ./configure –prefix=/usr/local/apache2 –with-included-apr –enable-so
    sudo make
    sudo make install
  5. Check its version:
    apache2 -v

Also check out “Install MariaDB / MySQL on Raspbian / Debian” and “Install PHP 7 from source on Raspbian/Debian

How To Unlock Desktop Screen After Remote Access Disconnected

Remote Desktop to VM to check out, investigate issues, deploy things are very common, especially when you are in CI/CD, Agile, DevOps environment. The demand to unlock desktop screen remotely is crucial for automation tests to run. A friend of mine says he struggled with this for 2 weeks. Therefore I am going to put simple and short answer right below.

How To Unlock Desktop Screen Remotely

Please note that you need to run these commands with administrative rights on remote machine.

Below command should work for Windows 7, Windows 8, Windows 8.1, Windows 10

tscon [RDC Session ID] /dest:console
Ex. tscon 1 /dest:console

Session ID can be retrieve by issuing below command

query session

Or simply use below single command

for /f "skip=1 tokens=2" %%s in ('query user %USERNAME%') do (
    tscon.exe %%s /dest:console


Below command should work on Windows Server

tscon %sessionname% /dest:console

If you have PowerShell installed on your remote machine, try this one

tscon ((quser $env:_TFSLab | select -Skip 1) -split '\s+')[2] /dest:console

Wrapping Up

That’s it. It’s simple when you know it and it can take you weeks if you don’t (like my friend’s case). Please share if you have any inputs or comment if these work for you.


Demystifying 3 Common Misconceptions About Xpath In Web Automation


Element identification lies at the core of automating web tests because without it, your test automation tool has no clue about how to locate and interact with the correct web elements on your application under test. As recommended by W3CXPath is today’s solution of choice widely adopted by many web automation solutions including the famous Selenium framework. However, just like mastering a katana, making use of XPath up to the proficient level requires quite some time and deliberate effort. This article aims to help you take full advantage of XPath by unveiling the 3 common yet deadly misconceptions which novice testers might pick up while learning this powerful “secret weapon”.

Miconceptions Unveilved

[1] XPath is an inherent property of the web element

Unraveling XPath this way is natural when you are learning the ropes of XPath but later on, it reveals itself as a dangerous analogy. Let’s say you’re copying the XPath of a button (<input>) on Chrome by right clicking on the element and choosing Copy > Copy XPath:

Copy XPath


This is what you get:


Meanwhile, if you use Firebug (an add-on of Firefox) on the same particular button, you will receive an utterly different result:


How on earth the XPath “property” of the same element is not even remotely similar provided that the element has not changed a wee bit?

It’s because XPath is indeed not a control’s “property” after all. By definition, XPath is a language to tell a browser, a test tool or any other pieces of software how to navigate an XML document and find the specified element. Hence, it’s very normal that different tools return different XPaths for the same exact element.

This implicates that it’s up to you to determine which XPath is the most readable yet reliable to identify an element.

Some test automation frameworks such as LogiGear’s TestArchitect™ offer a built-in feature to purposefully construct an elegant XPath to efficiently locate your web element. For instance, the screenshot below shows TestArchitect’s suggested XPath for the aforementioned button.

Interface Viewer


This path (//input[@id=’Login’]) is more elegant because:

  • It’s more specific than Chrome’s XPath (//*[@id=”Login”]) which accepts all tags as long as it has the predefined ID. Chrome’s suggestion would return the wrong element in case there happens to be a <td> with the identical ID (very likely).
  • It’s less fragile than Firebug’s XPath (/html/body/div/table/tbody/tr[5]/td/input) which could be easily broken (returns null) if let’s say, the <input> tag is somehow moved to another table row (e.g. tr[8])

No matter which suggestion is better than which, at the end of the day, you are still in charge of being the final gatekeeper.

[2] XPath is not reliable

This impression comes from the fact that sometimes your test run fails on a different browser (say Firefox) because the element captured using XPath on the original browser (say Chrome) cannot be found. A closer look explains that the button’s XPath on Chrome is actually:


While on Firefox, that same button’s XPath is:


This could be the case of dynamically generated elements which is quite popular in web development nowadays. Somewhere along the way, a developer has tied the creation of these web elements to a specific web browser. Although this practice doesn’t promote testability, it still exists out there in the wild.

If you didn’t pay attention, you could blame XPath for doing a lousy job. But in fact, the culprit is not XPath. Instead, it is the application’s peculiar method of generating different IDs on different browsers. While waiting for the developers to fix this inconsistency and improve the app’s testability, you can continue testing by adding a little tweak to your XPath as follows:

//input[contains(@id, ‘stdinput-00001‘) or contains(@id, ‘stdinput-0000A‘)]

Bravo! Your test can now run on both Chrome and Firefox.

[3] XPath is the silver bullet

Relying on XPath too much without carefully dissecting your web application would result in a big frustration when you receive unexpected failures. It’s worth emphasizing that one single XPath can fetch different elements in different contexts. For instance, let’s examine the following XPath:


What you want to interact with is the below <a> element:

Open Store


But to your surprise, it’s not the case in reality. Instead, this element is the one being clicked:

Open Store


Therefore, to be absolutely sure about what you get, you should invest in a good insight of the page’s hierarchy. In this particular example, you can easily distinguish the two elements based on their different ancestors.

XPath for the product’s name of the search result (case #1):


XPath for the breadcrumb button (case #2):



Hopefully unveiling these deadly misconceptions could help you understand XPath on a deeper level so that your tests will become more robust and automating web apps is as effortless as possible. Stay tuned for the upcoming article dedicated to XPath’s best practices.

Passing strings between MQL and C++ DLL

MetaTrader is a popular platform used for forex trading, and other financial products trading (stock, commodity…) It provides to user the ability to add more features to the platform using its own language MQL. You can program your own indicator and your expert advisor to work with MetaTrader. The powerful of MQL is that it can call a Windows DLL library, so that you can add any feature to your program.

Passing strings to exchange data between MQL and Windows DLL is the most difficult problem. How to exchange data buffer without causing MetaTrader crash? This can achieve by allocating buffer memory within MetaTrader, then pass its pointer to DLL. MetaTrader then has the rights to free the allocated memory. If the memory is allocated by DLL, MetaTrader can’t free it and it causes crashing problems or memory leak.

Here is an example about exchange a string using buffer memory:

Passing strings from MQL to C++ DLL

* MQL:

#import “MT4.dll”
void sendText(char&[]);

char buffer[10240];
StringToCharArray(“Hello World”, buffer);

* C++ DLL:

#define MT4EXPORT extern “C” __declspec(dllexport)

MT4EXPORT void sendText(char* buffer){
// do something with ‘buffer’

Passing strings from C++ DLL to MQL

* C++ DLL:

#define MT4EXPORT extern “C” __declspec(dllexport)
MT4EXPORT void getText(char* buffer){
char* text = “Hello World”;
strcpy(buffer, text);

* MQL:

#import “MT4.dll”
void getText(char&[]);

char buffer[10240];

string text=CharArrayToString(buffer);

Format a WD drive on MacOS

A friend of mine recommended WD drive My Passport (4TB) for me because I need one. The current one (SeaGate) is not stable, sometime it is not recognizable. I suspect it is due to the cable. Anyway I need more space for my massive data so I grabbed WD My Passport Ultra Metal Edition.

NTFS vs. Mac OS Extended (Journaled)

I use Windows at work, MacOS and sometimes Linux (CentOS) at home. Everything went smooth, I copied to backup 50+ GB of data in about 20 minutes.

The problem happened couple of minutes ago. I couldn’t copy files from my Mac to WD drive. I immediately recognize that is because the WD drive is formatted as NTFS by default. I reviewed the manual and it recommended user (me) to reformat WD drive before using on Mac. It did the same following this tutorial with no doubt and got this error. I tried couple of times and also tried it on another Mac machine and they led to same destination.

WD Drive Format

In my opinion, WD should update their how-to document or at least remove this one.

Get WD Drive My Passport back to work

I gave Support a call and it was out of their working hours unfortunately. At the time I am writing this article, I think I can call them at other zone which is in working hours. Anyway, I searched for solutions and here’s what I tried.

# First run
diskutil list

# To get the name to the disk you’re trying to format. The below commands assume this is “disk1”, but replace “disk1” with the correct disk if it’s something different.
# Now unmount the disk:
diskutil unmountDisk force disk1

# and then write zeros to the boot sector:
sudo dd if=/dev/zero of=/dev/disk1 bs=1024 count=1024

# Finally attempt to partition it again:
diskutil partitionDisk disk1 GPT JHFS+ “My External HD” 0g